(ICPC 2011 Asia Regional Beijing Site Online Contest) J Tourism Planning 题解

数据有点多有点乱的一道题,需要一些预处理,但是把思路层层理顺后DP思路就很显然了,用n位2进制数表示剩下的人的状态,第i位为1时表示有这人,为0时表示没有这个人。则状态有m*(2^n)个,状态转移比较容易。

这题可能测试数据很多,第一次超时了,1400ms左右,优化了输入降至1100ms,然后发现一个早该发现的优化,就是如果j状态能到达i状态,那么j>i是一定的。改过交,3A。这个应该可以早发现的,没有1A就是失败。

 

#include <cstdio>
#include <cstring>
int n,m;
int cost[12][2048];
int bonus[2048];
int profit[12][2048];
int dp[12][2048];
int v[12][12],p[12],b[12][12];
int posscome[1200][1200];
int pt[1200];
bool cango[1300][1300];
int maxbound;
char readintch;
void readint(int &x)
{
    while(readintch = getchar(),readintch<'0' || readintch>'9');
    x = readintch - '0';
    while(readintch = getchar(),readintch>='0' && readintch<='9')   x = x*10 + readintch - '0';
}
void calc_cost()
{
    int i,j,k,cnt;
    for(i=1;i<=m;i++)
    {
        for(j=0;j<=maxbound;j++)
        {
            cnt = 0; k=j;
            while(k!=0)
            {
                if(k&1)
                {
                    cnt++;
                }
                k = k>>1;
            }
            cost[i][j] = cnt*p[i];
        }
    }
}
void calc_bonus()
{
    int i,j,k;
    bool state[12];
    for(i=0;i<=maxbound;i++)
    {
        k = i;
        for(j=n;j>=1;j--)
        {
            state[j] = k&1;
            k = k>>1;
        }
        bonus[i] = 0;
        for(j=1;j<=n;j++)
        {
            for(k=j+1;k<=n;k++)
            {
                if(state[k] && state[j])
                {
                    bonus[i] += b[j][k];
                }
            }
        }
    }
}
void calc_profit()
{
    int i,j,k,p;
    bool state;
    for(i=1;i<=m;i++)
    {
        for(j=0;j<=maxbound;j++)
        {
            profit[i][j] = 0;
            k=j;
            for(p=n;p>=1;p--)
            {
                state = k&1;
                if(state)
                {
                    profit[i][j] += v[p][i];
                }
                k = k>>1;
            }
        }
    }
}
int ans = 0;
int max(int a,int b)
{
    return  (a>b?a:b);
}

bool test(int pos,int from,int to)
{
    if(pos == 1) return true;
    int k;
    for(k=n;k>=1;k--)
    {
        if( ( (from&1)==0) && ((to&1)==1))
            return false;
        from = from>>1;
        to = to>>1;
    }
    return true;
}

void calc_cango()
{
    int i,j,k,from,to;
    memset(pt,0,sizeof(pt));
    for(i=0;i<=1023;i++)
    {
        for(j=0;j<=1023;j++)
        {
            from = i,to = j;
            for(k=10;k>=1;k--)
            {
                if( ( (from&1)==0) && ((to&1)==1))
                {
                    cango[i][j] = false;
                    break;
                }
                from = from>>1;
                to = to>>1;
            }
            if(k==0)
            {
                cango[i][j] = true;
                posscome[j][pt[j]++] = i;
            }
        }
    }
    for(i=0;i<=10;i++)
    {
        printf("i: %d ",i);
        for(j=0;j<10;j++)
        {
            printf("%d ",posscome[i][j]);
        }
        printf("\n");
    }
    /*
    for(i=1;i<=5;i++)
    {
        for(j=1;j<=5;j++)
        {
            printf("%d ",cango[i][j]);
        }
        printf("\n");
    }
    */
}

void makedp()
{
    int i,j,k,tmp,tmpk;
    for(i=0;i<=m;i++)
    {
        for(j=0;j<=maxbound;j++)
            dp[i][j] = -1000000000;
    }
    dp[0][0] = 0;
    for(i=1;i<=m;i++)
    {
        for(j=0;j<=maxbound;j++)
        {
            if(i==1)
                dp[i][j] = profit[i][j] + bonus[j] - cost[i][j];
            else
            {
                for(tmpk = 0;tmpk<pt[i] && posscome[i][tmpk] <=maxbound;tmpk++)
                //for(k=0;k<=maxbound;k++)
                {
                    //if(cango[k][j])
                    k = posscome[i][tmpk];
                        tmp = dp[i-1][k] + profit[i][j] + bonus[j] - cost[i][j];
                        if(tmp > dp[i][j])
                            dp[i][j] = tmp;
                }
            }

            if(dp[i][j] > ans)
            {
                ans = dp[i][j];
                //printf("ans: %d i:%d j:%d\n",ans,i,j);
            }

        }
    }
}

int main()
{
    int i,j,k;
    calc_cango();
    while(readint(n),readint(m),n||m)
    {
        ans = -1;
        maxbound = (1<<n)-1;
        for(i=1;i<=m;i++)
            readint(p[i]);
            //scanf("%d",&p[i]);
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                readint(v[i][j]);
                //scanf("%d",&v[i][j]);
            }
        }
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                readint(b[i][j]);
                //scanf("%d",&b[i][j]);
            }
        }
        calc_cost();
        calc_bonus();
        calc_profit();
        makedp();
        if(ans <= 0)
        {
            printf("STAY HOME\n");
        }
        else
        {
            printf("%d\n",ans);
        }
        for(i=1;i<=m;i++)
        {
            for(j=0;j<=maxbound;j++)
            {
                printf("%d %d %d\n",i,j,dp[i][j]);
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/nkhelloworld/archive/2011/09/18/2180773.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值