HLG 1473 教主的遗产【状态压缩DP】

题意:

在比赛中会给出n个题,不同做题顺序会有不同的AC系数,假如A先做,B后做的话,B对A的AC系数为4, 反过来 B先做,A后做的话,A对B的AC系数为5,说明先做B后做A将得到更高的AC系数。

设Sij表示第i题在第j题做完后才做获得的AC系数,有三道题a, b, c,做题顺序为bac,则系数和为:Sum = Sab + Scb + Sca。

求一个做题顺序,使得AC系数和最大。
分析:  开始当成是 环状的状态DP ,WA了几个点T T ;
           用dp[stat][k] 表示在 状态stat 下以k题结尾的最大分数,k包含在状态stat中
       f[stat][k] 表示在 状态stat 下,stat中不包含k题,k与stat中的题目系数和
           状态转移方程 dp[stat][k]=f[s][k]+max(dp[s][j])  j 为 状态S中包含的题目  
PS:  stat 表示状态 比如 n 为 3 ,当 stat=6 的时候,要把stat 看成是 110  这个二进制状态,可以看出因为 stat&(1<<1)为非0
      stat&(1<<2)也为非0 ,所以 stat 这个状态中包含第一题和第二题,而不包含第三题。
#include<stdio.h>
#include<string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int dp[1<<16][17];
int g[17][17];
int f[1<<16][17];
int main()
{
  //  freopen("D:ce.txt","r",stdin);
    int st,stat,i,j,k,x,y,n;
    int z1,z2;
    while(scanf("%d",&n)!=EOF)
    {
        st=(1<<n)-1;
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&g[i][j]);
        memset(dp,0xff,sizeof(dp));
        memset(f,0,sizeof(f));
        for(i=0;i<n;i++)
            dp[1<<i][i]=0;
        int stat;
        int tt,t2;
        int res=0;              
        for(i=0;i<st;i++)                      // f[i][k] 表示在 i 这个状态后面 增加一个 k 题后 得到的分数,其中 i 中不能包含 k 这个状态
            for(k=0;k<n;k++)                   // 例如当 n 为3,i 为 2 的时候,i 看成二进制 010 ,这时 k 可以是 0 和2(也就是第 一题和第三题),
            {                                  
                if((1<<k)&i)                   // 所以 f[2(010)][1]=g[0][1]+g[2][1]     
                    continue;
                for(j=0;j<n;j++)
                    if(i&(1<<j))
                        f[i][k]+=g[k][j];
            }
        for(i=0;i<st;i++)
            for(j=0;j<n;j++)
            {
                if(dp[i][j]<0)                     // i 这个状态 里要包含 j 这题
                    continue;
                tt=0;
                for(z1=0;z1<n;z1++)
                    if((1<<z1)&i)
                        tt=max(tt,dp[i][z1]);      //  找出 i 这个状态中 的最高分数
                for(k=0;k<n;k++)
                {
                    if(i&(1<<k))                   //  i 这个状态了 不能包含 k 这个题
                        continue;
                    stat=i+(1<<k);
                    dp[stat][k]=tt+f[i][k];        //  dp[stat][k]=f[s][k]+max(dp[s][j]) // f[s][k] 表示 s 的状态下,后面加 k 这题时增加的分数 
                }                                                                        // j 属于 0 到 n-1,max(dp[s][j])目的是找出 s 状态中最高 
            }                                                                              // 的分数
        for(i=0;i<n;i++)
            res=max(res,dp[st][i]);
        printf("%d\n",res);
    }
    return 0;
}

 

posted @ 2012-07-19 19:51  'wind  阅读(278)  评论(0编辑  收藏  举报