CSU 1592 石子归并(记忆化搜索 or 区间DP)

题目链接

记忆化搜索

  这是一道区间DP的模板题。初学dp的话还是记忆化搜索好理解一些,很多dp其实也是从记忆化搜索转换过来的。考虑最后一步,我们要把两堆石子合并成一堆,那么肯定是某一个点为分割点分成的两堆石子,而这两堆石子也是由某一个点为分割点组成的,如此递推下去。所以我们倒着把一堆石子拆成两堆,每次枚举分割点,最后回溯的时候取最小值就行了。

代码

const int maxn = 1e2+10;
int pre[maxn], rec[maxn][maxn];
int dfs(int l, int r) {
    if (l==r) return 0;
    cout << l << ' ' << r << endl;
    if (rec[l][r]!=INF) return rec[l][r];
    int res = INF, cost = pre[r]-pre[l-1];
    for (int i = l; i+1<=r; ++i)
        res = min(res, dfs(l, i)+dfs(i+1, r)+cost);
    return rec[l][r] = res;
}
int main() {
    IOS; int t; cin >> t;
    while(t--) {
        int n; cin >> n; INF(rec); zero(pre);
        for (int i = 1; i<=n; ++i)  {
            cin >> pre[i];
            pre[i] += pre[i-1];
        }
        cout << dfs(1,n) << endl;
    }
    return 0;
}

dp

  我们看看之前的记忆化搜索不难发现,其最终是递归到最小的区间(大小为2)然后开始回溯的,所以我们如果要dp的话,就应该从最小的区间开始枚举,然后逐步往大的区间扩展。

代码

const int maxn = 1e2+10;
int pre[maxn], dp[maxn][maxn];
int main() {
    int t; cin >> t;
    while(t--) {
        int n; cin >> n; zero(pre); zero(dp);
        for (int i = 1; i<=n; ++i)  {
            cin >> pre[i];
            pre[i] += pre[i-1];
        }
        for (int len = 1; len<n; ++len) 
            for (int l = 1; l+len<=n; ++l) {
                int r = l+len;
                dp[l][r] = INF;
                for (int k = l; k+1<=r; ++k)
                    dp[l][r] = min(dp[l][r],  dp[l][k]+dp[k+1][r]+pre[r]-pre[l-1]);
            }
        cout << dp[1][n] << endl;
    }
    return 0;
}
posted @ 2020-05-19 11:54  shuitiangong  阅读(175)  评论(0编辑  收藏  举报