您的位置:首页 > 其它

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

2017-08-29 15:01 375 查看
题意:

一个监狱里有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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: