CCPC2019河北省赛 C.分治(区间DP、记忆化搜索)

  • 题目:分治
  • 题意:中文题,十分简洁明了~
  • 解析:这道题十分显然用区间dp来做(本质上就是求每段区间的最小花费,最后的总花费必然最小),但值得注意的是区间越界问题
    1. 集合划分:f[l][r]表示:攻占完l->r的国家所需的最小花费
    2. 状态转移(实际上在转移过程中每一次枚举占领的城市可视为它就是第一个要占领的城市):
      1. 不占领该国家:f[l][r]
      2. 占领该国:f[l][max(k-1, l)] + f[min(k+1, r)][r] + (r - l) * a[k](取max只是防止越界问题,加上的值为需要给周围所有国家付的费用)
      3. 取两者max即可
  • ps:假如用记忆化搜索来写可能助于理解(还不用怕越界),每一次访问的[l,r]区间的所有国家都未被占领,枚举占领每一个国家后去搜索占领左侧所有国家和右侧所有国家需要的最小费用即可。
  • Dp代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 105;
const int inf = 0x3f3f3f3f;
int f[N][N], a[N];
int main()
{
    int t, n;
    cin >> t;
    while(t --)
    {
        memset(a, 0, sizeof a);
        memset(f, 0x3f, sizeof f);
        cin >> n;
        for(int i = 1; i <= n; i++)
        {
            cin >> a[i];
            f[i][i] = 0;
        }
        for(int len = 2; len <= n; len++)
        {
            for(int l = 1; l + len - 1 <= n; l ++)
            {
                int r = l + len - 1;
                for(int k = l; k <= r; k++)
                {
                    f[l][r] = min(f[l][r], f[l][max(k-1, l)] + f[min(k+1, r)][r] + (r - l) * a[k]);
                }
            }
        }
        cout << f[1][n] << endl;
    }
    return 0;
}

  • 记忆化搜索代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 105;
const int inf = 0x3f3f3f3f;
int f[N][N], a[N];
int dfs(int l, int r)
{
    if(l >= r) return 0; //判断越界
    if(f[l][r] != inf) return f[l][r];
    for(int k = l; k <= r; k++)
        f[l][r] = min(f[l][r], dfs(l, k -1) + dfs(k + 1, r) + (r - l) * a[k]);
    return f[l][r];
}
int main()
{
    int t, n;
    cin >> t;
    while(t --)
    {
        memset(a, 0, sizeof a);
        memset(f, 0x3f, sizeof f);
        cin >> n;
        for(int i = 1; i <= n; i++)
        {
            cin >> a[i];
            f[i][i] = 0;
        }
        cout << dfs(1, n) << endl;
    }
    return 0;
}

posted @ 2021-05-21 11:19  ~K2MnO4  阅读(180)  评论(0)    收藏  举报