POJ 1036 Gangsters DP 多解
题意:一群匪徒要进入一个酒店。酒店的门有k+1个状态,每个匪徒的参数是:进入时间,符合的状态,携带的钱。
酒店的门刚开始状态0,问最多这个酒店能得到的钱数。
思路1:dp[i][j]表示时间0-i之间,门的状态为j时所能获得的最大利益
转移方程 :dp[i][j]=max(dp[i-1][j],dp[i-1][j-1],dp[i-1][j+1])
因为转移i只跟i-1有关,所以可以用滚动数组dp[2][k]
有一个处理边界的好方法:
j的范围 0 —k,不妨向右移动1位,并保证0,k+2时的状态为0
代码1:
View Code
//O(k*t) #include<stdio.h> #include<string.h> int max(int a, int b) { return a > b ? a : b;} int tt[103], s[103], p[103], dp[2][103], a[30004][103]; int main() { int i, j; int n, k, t; while( ~scanf("%d%d%d", &n, &k, &t)) { for(i = 1; i <= n; i++) scanf("%d", &tt[i]); for(i = 1; i <= n; i++) scanf("%d", &p[i]); for(i = 1; i <= n; i++) scanf("%d", &s[i]); memset(dp, 0, sizeof(dp)); memset(a, 0, sizeof(a)); for(i = 1; i <= n; i++) if(tt[i] >= s[i]) //把一定不能进入的删去,它会影响状态转移 //保证0,k+2时的状态为0 a[tt[i]][s[i]+1] += p[i]; for(i = 1; i <= t; i++) for(j = 1; j <= k+1 && j <= i+1; j++) dp[i&1][j] = max( dp[(i-1)&1][j+1], max(dp[(i-1)&1][j-1], dp[(i-1)&1][j]) ) + a[i][j]; int ans = 0; for(i = 1; i <= k+1; i++) ans = max(ans, dp[t&1][i]); printf("%d\n", ans); } return 0; }
代码2:
View Code
//O(k*n) #include<stdio.h> #include<string.h> int max(int a, int b) { return a > b ? a : b;} int tt[103], s[103], p[103], dp[30003][103]; int main() { int i, j; int n, k, t; while( ~scanf("%d%d%d", &n, &k, &t)) { for(i = 1; i <= n; i++) scanf("%d", &tt[i]); for(i = 1; i <= n; i++) scanf("%d", &p[i]); for(i = 1; i <= n; i++) scanf("%d", &s[i]); memset(dp, 0, sizeof(dp)); for(i = 1; i <= n; i++) if(tt[i] >= s[i]) //同代码1 dp[tt[i]][s[i]+1] += p[i]; for(i = 1; i <= t; i++) for(j = 1; j <= k+1 && j <= i+1; j++) dp[i][j] += max( dp[(i-1)][j+1], max(dp[(i-1)][j-1], dp[(i-1)][j]) ); int ans = 0; for(i = 1; i <= k+1; i++) ans = max(ans, dp[t][i]); printf("%d\n", ans); } return 0; }
思路2:
仔细一想思路1发现dp数组中时间(t)那一维的空间有很多不必要的操作,因此我们可以将数组的这一维优化到人数(n)。
先将n个人按时间从小到大排序。
dp[i][j]表示进行到第i个人,门的状态为j时所获得的最大利益
其实这里的思想是单调队列优化,这样1->2->3->....->n递推下去,。
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; struct node { int t, p, s; }g[103]; int max(int a, int b) { return a > b ? a : b;} int abs(int a) { return a > 0 ? a : -a; } bool cmp(node a, node b) { return a.t < b.t; } int dp[103][103]; int main() { int i, j; int n, k, t; while( ~scanf("%d%d%d", &n, &k, &t)) { for(i = 1; i <= n; i++) scanf("%d", &g[i].t); for(i = 1; i <= n; i++) scanf("%d", &g[i].p); for(i = 1; i <= n; i++) scanf("%d", &g[i].s); sort(g+1, g+n+1, cmp); memset(dp, -1, sizeof(dp)); g[0].s = g[0].t = 0; dp[0][0] = 0; for(i = 0; i < n; i++) { for(j = 0; j <= k; j++) { if(dp[i][j] == -1) continue; for(int u = 0; u <= k; u++) if(abs(j-u) <= g[i+1].t - g[i].t) dp[i+1][u] = max(dp[i+1][u], dp[i][j]); } if(dp[i+1][g[i+1].s] != -1) dp[i+1][g[i+1].s] += g[i+1].p; } int ans = -1; for(i = 0; i <= k; i++) ans = max(ans, dp[n][i]); printf("%d\n", ans); } return 0; }
以上的情况,还可以再优化
用dp[i]表示表示进行到第i个人所获得的最大利益
递推做了改变, i的状态可以是从 0---(i-1)这些状态推过来
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int ABS(int a) { return a > 0 ? a : -a; } int max(int a, int b) { return a > b ? a : b; } struct node { int t,p,s; }g[101]; bool cmp(node a, node b) { return a.t<b.t; } int dp[105]; int main() { int i,j,k,p,q,m,n,t; while(scanf("%d%d%d", &n, &k, &t)!=-1) { memset(dp, 0, sizeof(dp)); for(i = 0; i < n; i++) scanf("%d", &g[i].t); for(i = 0; i < n; i++) scanf("%d", &g[i].p); for(i = 0; i < n; i++) scanf("%d", &g[i].s); sort(g, g+n, cmp); for(i=0;i<n;i++) { dp[i] = (g[i].t>=g[i].s)? g[i].p : 0; for(j = 0; j < i; j++) { if(!dp[j]) continue; if(ABS(g[i].s-g[j].s) > g[i].t-g[j].t) continue; dp[i] = max(dp[i], dp[j]+g[i].p); } } int ans = 0; for(i = 0; i < n; i++) ans = max(ans, dp[i]); printf("%d\n",ans); } return 0; }


浙公网安备 33010602011771号