Bribe the Prisoners(GCJ 2009 Round 1C C)(dp)

题意:

一个监狱里有P个并排着的牢房。从左至右依次编号为1,2,...,P。最初所有的牢房里都住着一个囚犯。相邻的两个牢房之间可以互通信息。

现在要释放一些囚犯。如果释放某个牢房里的囚犯,其相邻的牢房里的囚犯就会知道,因而发生暴动。所以,释放某个囚犯时,必须要贿赂两旁相邻牢房的囚犯一枚金币。另外,为了防止释放的消息在相邻牢房间传开,不仅两旁直接相邻的牢房,所有可能听到消息的囚犯,即直到空牢房为止或直到监狱两端为止,此间的所有囚犯都必须给一枚金币。

现在要释放Q名囚犯。如果选择所需金币数量尽量少的顺序释放,最少需要多少枚金币?


释放的Q名囚犯,显然会将整个区间分成许多小区间,以此为思路,我们用dp[i][j]表示:将从a[i]号囚犯到a[j]号囚犯(不含两端的囚犯)的连续部分里的所有囚犯都释放时,所需的最少金币总数。

为了更方便的处理两端的情况,我们把左端当成0号囚犯,右端当成Q + 1号囚犯。这样,dp[0][Q + 1]就是答案。


代码:

#include <cstdio>  
#include <algorithm>  
using namespace std;  
const int maxn = 10000 + 10;  
const int INF = 10000000;  
  
int P, Q, a[maxn];    //A中保存输入数据,下标从1开始  
int dp[maxn][maxn];   //dp[i][j] := 释放(i, j)所需的金币  
int main()  
{  
    int T;  
    scanf("%d", &T);  
    for (int cas = 1; cas <= T; cas++){  
        scanf("%d%d", &P, &Q);  
        for (int i = 1; i <= Q; i++){  
            scanf("%d", &a[i]);  
        }  
  
        //为了方便,将两端加入a中  
        a[0] = 0;  
        a[Q + 1] = P + 1;  
  
        //初始化  
        for (int q = 0; q < Q; q++){  
            dp[q][q + 1] = 0;  
        }  
  
        //从短的区间开始填充dp  
        for (int w = 2; w <= Q + 1; w++){  
            for (int i = 0; i + w <= Q + 1; i++){  
                //计算dp[i][j]  
                int j = i + w, t = INF;  
                //枚举最初释放的囚犯,计算最小的费用  
                for (int k = i + 1; k < j; k++){  
                    t = min(t, dp[i][k] + dp[k][j]);  
                }  
  
                //最初的释放还需要与所释放囚犯无关的a[j] - a[i] - 1 - 1枚金币  
                dp[i][j] = t + a[j] - a[i] - 1 - 1;  
            }  
        }  
        printf("Case #%d: %d\n", cas, dp[0][Q + 1]);  
    }  
    return 0;  
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值