GCJ 2009 Round 1C C (Bribe the Prisoners)

題意:

 

有P個相鄰的牢房(序號1,2,…,P),每個牢房中關押着犯人一名

現在要釋放Q個牢房中的犯人(序號給出),釋放後牢房爲空

每次釋放一名時需要依次向左向右給其他牢房犯人每人一枚金幣直到遇到空牢房爲止

 

可以按照任何順序釋放犯人,輸出所需最小金幣數。

 

思路:

爲方便理解,預處理出序號爲0和序號爲P+1的空牢房。

可以想到,如若釋放了序號i(0<i<P+1)號牢房的犯人,則對0到i牢房組和 i到P+1牢房組的討論完全獨立且其最優解可構成0到P+1的最優解(算導將其稱作最優子結構)

 

故可以用動態規劃的思想:

 

定義狀態dp[i][j] (0<=i<=j<=P+1) 爲釋放從i到j之間(不包括i,j)的所有需釋放的犯人所需最小金幣數(i與j之間無空牢房)

 

則可得轉移方程爲

 

考慮遞歸樹的底層,轉移順序應該從短區間開始轉移(枚舉區間長度即可)

 

d[i][j] = (j-i-1)+d[i][v]+d[v][j] (i<v<j)

 

d[0][P+1]即爲所求最優解。

 

P.S. 此處的未釋放但需釋放的犯人可以理解成隱式的空牢房。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define LL long long
#define MM 10010
#define MAX 1e16

LL dp[MM][MM];
int free_num[MM];

using namespace std;

int main()
{
	int P, Q;
	while(scanf("%d %d", &P, &Q)==2)
	{
		memset(dp, 0, sizeof(dp));

		for (int i = 1; i <= Q; ++i)
			scanf("%d", &free_num[i]);

		free_num[0] = 0;
		free_num[Q+1] = P+1;


		for (int diff = 2; diff <= Q+1; ++diff)
			for (int head = 0; head+diff <= Q+1; ++head)
                        {
                            LL temp = MAX;
                            int tail = head + diff;

			    for (int v = head+1; v < tail; ++v)
			        temp = min(temp, dp[head][v] + dp[v][tail]);

                            dp[head][tail] = temp + free_num[tail] - free_num[head] - 2;
                        }

		printf("%d\n", dp[0][Q+1]);
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章