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;
}

 

 

 

 

posted @ 2012-11-10 19:44  To be an ACMan  Views(1107)  Comments(0)    收藏  举报