题意:有p个犯人,编号从1到p,现有q个犯人可以被释放,但是他两旁的所有犯人看到了觉得很不爽,所以要给1元钱来贿赂他们(请问他在监狱里到哪去花钱),一直到一个空监狱,要求安排q个犯人释放的顺序,使得总代价最小
题解:
区间dp
状态:dp[i][j]表示(i,j)的犯人被释放的最小代价
转移:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+v[j]-v[i]-2)
初值:dp[i][i+1]=0
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #define ll long long 8 using namespace std; 9 10 const int N = 110; 11 12 int T,n,m,tot; 13 int v[N],dp[N][N]; 14 15 int gi() { 16 int x=0,o=1; char ch=getchar(); 17 while(ch!=‘-‘ && (ch<‘0‘ || ch>‘9‘)) ch=getchar(); 18 if(ch==‘-‘) o=-1,ch=getchar(); 19 while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 20 return o*x; 21 } 22 23 int main() { 24 T=gi(); 25 while(T--) { 26 int p=gi(),q=gi(); 27 for(int i=1; i<=q; i++) v[i]=gi(); 28 v[q+1]=p+1; 29 memset(dp,63,sizeof(dp)); 30 for(int i=0; i<=q; i++) dp[i][i+1]=0; 31 for(int l=1; l<=q; l++) 32 for(int i=0; i<=q+1; i++) { 33 int j=i+l+1; 34 for(int k=i+1; k<=j-1; k++) { 35 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+v[j]-v[i]-2); 36 } 37 } 38 printf("Case #%d: %d\n", ++tot,dp[0][q+1]); 39 } 40 return 0; 41 }
总结:区间DP
这里的递推实际上是正常操作的一个逆过程
也就是相当于从子问题推出全局问题
所以要先推出范围较小一点的子区间
于是第一维枚举范围,第二维枚举起点,算出终点,第三维枚举中间点转移
时间: 2024-10-12 13:05:34