fzoj 1319 Blocks of Stones(DP:水题)

Accept: 183    Submit: 732
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

There are n blocks of stones in a line laying on the ground. Now you are to merge these blocks of stones together with the restriction that you can only merge the neighbouring blocks each time. The score you get for each merging is the number of stones the new blocks has.

Before merging, you are allowed to swap a neighbouring block. You are to calculate the minimal score you will get during the merging process, and the swap position.

 Input

There are multiple test cases. For each case, the first line contains a integer n(n<=100), representing the number of blocks. The second line contains n integers, representing number of stones in each blocks

 Output

For each case, output 2 lines. The first line contains a single integer, representing the minimal score. The second line contains to integers in the form "(k,k+1)", representing the swap position. If there is more than one swap position which can be the minimal score, output the one with the largest k.  You should note that : If swap is not needed output "(0,1)".

 Sample Input

32 5 13 1 2 5

 Sample Output

11(2,3)11(0,1)

 Source

chenyan




几个月没写DP,真的是废了。。。

这个题很蛋疼的一点就是还存在交换相邻位置的情况

令s[i]保存1-i项石子总数,则交换a[i],a[i+1]即为s[i] = s[i]-a[i]+a[i+1];

枚举n次交换即可,还要注意在一次交换后要恢复原来的情况

状态转移方程为:

dp[i][j] = min(dp[i][k]+dp[k+1,j], dp[i][j])+s[j]-s[i-1];

这个题先写的记忆化搜索,总是超时(后来才知道写的是纯搜索,没有记忆化尴尬

后来又写DP,真是蛋碎了一地!

代码如下:

#include <stdio.h>
#include <string.h>
#define MAXN 110
#define INF 0x7fffffff

int a[MAXN], s[MAXN], dp[MAXN][MAXN];

int main(void) {
    int tmp, ans, res, x, n, i, j, k, r, t;
    while(scanf("%d", &n) != EOF) {

        s[0] = 0;
        memset(a, 0, sizeof(a));
        
        for(i=1; i<=n; ++i) {
            scanf("%d", &a[i]);
            dp[i][i] = 0;
            s[i] = s[i-1]+a[i];
        }

        ans = INF;
        //dp[i][j] = min(dp[i][k]+dp[k+1,j], dp[i][j])+s[j]-s[i-1];

        for(t=0; t<n; ++t) {
            if(t>0)  s[t] = s[t]-a[t]+a[t+1];

        for(r=1; r<n; ++r) {
            //printf("s[%d] = %d\n", r, s[r]);
            for(i=1; i<=n-r; ++i) {
                j = i+r;
                dp[i][j] = INF;
                for(k=i; k<j; ++k) {
                    tmp = dp[i][k]+dp[k+1][j];
                    dp[i][j] = dp[i][j]>tmp ? tmp : dp[i][j];
                }
                dp[i][j] += s[j]-s[i-1];
            }
        }

        //printf("dp[1][%d] = %d\n", n, dp[1][n]);
        tmp = dp[1][n];
        if(tmp <= ans) {
            ans = tmp;
            x = t;
        }
        if(t>0) s[t]=s[t]-a[t+1]+a[t];//恢复变化前的状态,t==0时为最初不交换的状态
        else res = dp[1][n];
        
        }

        if(ans == res) x = 0;//如果和最初的结果一样还要令x=0

        printf("%d\n", ans);
        printf("(%d,%d)\n", x, x+1);
        
    }
}

我把搜索改进为记忆化搜索代码的如下:

#include <stdio.h>
#include <string.h>
#define MAXN 110
#define INF 0x7fffffff

int a[MAXN], s[MAXN], dp[MAXN][MAXN];

int search(int i, int j) {
    int ans, tmp, k;
    if(dp[i][j] != -1) return dp[i][j];
    ans = INF;
    for(k=i; k<j; ++k) {
        tmp = search(i, k)+search(k+1, j);
        ans = ans > tmp ? tmp : ans;
    }
    return dp[i][j] = ans + s[j]-s[i-1];
}

int main(void) {
    int tmp, ans, res, x, n, i;
    while(scanf("%d", &n) != EOF) {

        s[0] = 0;
        memset(dp, -1, sizeof(dp));
        memset(a, 0, sizeof(a));
        
        for(i=1; i<=n; ++i) {
            scanf("%d", &a[i]);
            s[i] = s[i-1]+a[i];
            dp[i][i] = 0;
        }

        x = 0;
        search(1, n);
        ans = res = dp[1][n];

        for(i=1; i<n; ++i) {
            s[i] = s[i]-a[i]+a[i+1];
            search(1, n);
            if(ans >= dp[1][n]) {
                ans = dp[1][n];
                x = i;
            }
            s[i] = s[i]-a[i+1]+a[i];
            memset(dp, -1, sizeof(dp));
            for(int j=1; j<=n; ++j)
                dp[j][j] = 0;
        }
        
        if(res == ans)
            x = 0;

        printf("%d\n", ans);
        printf("(%d,%d)\n", x, x+1);
        
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值