HDU4283You Are the One区间dp,
记忆话搜索运行时间:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 105,inf = 1<<30; int dp[maxn][maxn][maxn],arr[maxn],n; int solve(int s,int e,int k){ if( s == e)return arr[s] * (k-1); if( s > e)return 0; if(dp[s][e][k] != inf)return dp[s][e][k]; for(int i = s; i <= e; i++){ //将区间[s,e]划分为[s,i],[i+1,e] int nextk = k+(i-s)+1; //[i+1,e]区间内第i个人第nextk个出栈 int thisk = k+(i-s); //[s,i]区间的第s个人第thisk个出栈 int first = solve(s+1,i,k); //第1个区间经过分配得到最少屌丝值 int second = solve(i+1,e,nextk); //第2个区间经过分配得到的最少屌丝值 int cur = arr[s] * (thisk-1); //第s个人第thisk出栈增加的屌丝值 dp[s][e][k] = min(dp[s][e][k],cur+second+first); } return dp[s][e][k]; } int main(){ int t,cas = 1; scanf("%d",&t); while( t-- ){ scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",arr+i); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) for(int k = 0; k <= n; k++) dp[i][j][k] = inf; printf("Case #%d: %d ",cas++,solve(1,n,1)); } return 0; }
三个for循环
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn =105; const int inf = 1<<30; int dp[maxn][maxn],arr[maxn],sum[maxn]; int n; int solve_dp(){ for(int len = 1; len < n; len++){ //枚举区间长度 for(int s = 1; s + len <= n; s ++){ //枚举区间起点 int end = s+len; //s为区间头,end为区间尾 for(int k = s; k <= end; k++){ //第k个出栈 int tp = arr[s] * (k-s); tp += dp[s+1][k] + dp[k+1][end]; tp += (k-s+1) * (sum[end] - sum[k]);//重要的性质,如果第i个人第k个出栈,那么后面的人出栈顺序都大于k dp[s][end] = min(dp[s][end],tp); } } } return dp[1][n]; } int main(){ int t,cas = 1; scanf("%d",&t); while( t-- ){ scanf("%d",&n); for(int i = 1; i <= n; i++){ scanf("%d",arr+i); sum[i] = sum[i-1] + arr[i]; } memset(dp,0,sizeof(dp)); for(int i = 1; i <= n; i++) for(int j = i+1; j <= n; j++) dp[i][j] = inf; printf("Case #%d: %d ",cas++,solve_dp()); } return 0; }
经典的区间DP模型--最大括号匹配数。如果找到一对匹配的括号[xxx]oooo,就把区间分成两部分,一部分是xxx,一部分是ooo,然后以此递归直到区间长度为1或者为2.
状态转移方程:if(mach(j,j+i))dp[j][j+i] = dp[j+1][j+i-1]+2;
dp[j][j+i] = max(dp[j][k],dp[k+1][j+i])(j <= k < j+i)
/* ** 一种自底向上型的DP ** 状态转移方程:if(mach(j,j+i))dp[j][j+i] = dp[j+1][j+i-1]+2; ** dp[j][j+i] = max(dp[j][k],dp[k+1][j+i])(j <= k < j+i) */ #include <stdio.h> #include <cstring> #define Max(a,b) (a)>(b)?(a):(b) #define maxn 101 int dp[maxn][maxn]; char str[maxn]; int main() { while(~scanf("%s",str)) { if(strcmp(str,"end") == 0)break; int len = strlen(str); memset(dp,0,sizeof(dp)); for(int i = 1; i <= len; i++) {//枚举区间长度 for(int j = 0; j <= len - i; j++) {//枚举起点 dp[j][j+i] = dp[j+1][j+i-1] ; if((str[j] == '(' && ')'== str[j+i]) || (str[j] =='[' && str[j+i] == ']' )) dp[j][j+i] += 2; for(int k = j; k < i+j; k++)//枚举区间j->j+i的分界点 dp[j][j+i] = Max(dp[j][j+i],dp[j][k]+dp[k+1][j+i]); } } printf("%d ",dp[0][len-1]); } return 0; }
Light OJ 1422 Halloween Costumes
很基础的区间DP,是不老传说那题的减弱版。
状态转移方程:
dp[s][e] = dp[s][e-1] (arr[s] == arr[e]).dp[s][e] = dp[s][e-1]+1 (arr[s] != arr[e]) ;
dp[s][e] = min(dp[s][e] ,dp[s][k]+dp[k+1][e]);(arr[s] == arr[k] && s <= k < e )
dp[s][e] = min(dp[s][e] ,dp[s][k]+dp[k+1][e]+1);(arr[s] != arr[k] && s <= k < e)
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 110; int dp[maxn][maxn],arr[maxn]; int main() { int t,cas = 1,n; scanf("%d",&t); while( t-- ) { scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&arr[i]); memset(dp,0,sizeof(dp)); for(int i = 1; i <= n; i++)dp[i][i] = 1; for(int i = 1; i <= n; i++) for(int j = 1; j <= n-i; j++) { dp[j][j+i] = dp[j][j+i-1]; if(arr[j] != arr[j+i])dp[j][j+i]++;//当起点和终点不同时衣服数量加1 for(int k = j; k < j+i; k++) { if(arr[j] == arr[k]) dp[j][j+i] = min(dp[j][j+i],dp[j][k]+dp[k+1][j+i]);//当第j和第k相等时,衣服数量等于(j->k) + (k+1->j+i) else dp[j][j+i] = min(dp[j][j+i],dp[j][k]+dp[k+1][j+i]+1);//当j和k不相等时,衣服数量等于(j->k) + (k+1->j+i) + 1 } } printf("Case %d: %d ",cas++,dp[1][n]); } return 0; }