题意:给定n(n<=100)个人,每一个有固定的权值D,现在n个人从1~n排成一排,第i个人第j个出列的时候的unhappiness值是Di* (j-1),现在有一个栈,
可以暂时不让当前的人出列将其压栈,问怎样安排能使得sigma(unhappiness i)最小,求这个最小值。
题解:想dp[i][j]表示[i ,j]内的unhappiness最小值,枚举k(i<=k<j),有两种情况需要讨论:
1 在k时刻[i , k]区间内的人全部在[k+1, j]区间内的人之前出列,且已经全部不在栈中,即[i , j]区间可以分为[i , k] , [k+1 ,j]两个完全相同的子问题,
即dp[i][j] =MIN(dp[i][j] , dp[i][k] + dp[k+1][j] + (sum[j] – sum[i]) * (k – i +1));
2 在k时刻[i , k]区间内的人全部在[k+1 , j]区间内的人之后出列,即[i , k]区间内的人全部需要进栈,所以出来的顺序是逆序的,需O(n2)预处理出against_order[i][j]
表示[i , j]区间人逆序出来的unhappiness值,即dp[i][j] = MIN(dp[i][j] , dp[k+1][j] + against_order[i][k] + (sum[k]– sum[i-1]) * (j - k));
Sure原创,转载请注明出处。
#include <iostream> #include <cstdio> #include <memory.h> #define MIN(a , b) ((a) < (b) ? (a) : (b)) using namespace std; const int inf = 1 << 29; const int maxn = 102; int diao[maxn],sum[maxn],against_order[maxn][maxn],dp[maxn][maxn]; bool vis[maxn][maxn]; int n; void read() { memset(vis,false,sizeof(vis)); sum[0] = 0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&diao[i]); sum[i] = sum[i-1] + diao[i]; against_order[i][i] = 0; } return; } void make() { for(int k=1;k<n;k++) { for(int i=1;i<n;i++) { int j = i + k; if(j > n) break; against_order[i][j] = against_order[i+1][j] + diao[i] * (j - i); } } return; } int dfs(int st,int end) { if(st == end) { vis[st][end] = true; return dp[st][end] = 0; } if(vis[st][end]) { return dp[st][end]; } int res = inf; for(int i=st;i<end;i++) { res = MIN(res , dfs(st , i) + dfs(i+1 , end) + (sum[end] - sum[i]) * (i - st + 1)); res = MIN(res , dfs(i+1 , end) + against_order[st][i] + (sum[i] - sum[st-1]) * (end - i)); } vis[st][end] = true; return dp[st][end] = res; } int main() { int cas; scanf("%d",&cas); for(int i=1;i<=cas;i++) { read(); make(); printf("Case #%d: %d\n",i,dfs(1,n)); } return 0; }