状态压缩动态规划感觉都不是那么好写,看网上的人说这题是2011年ACM/ICPC中的水题,暗地里感觉很是惭愧啊(花了将近4个小时),结果还算是勉勉强强地弄出来了。
与往常一样,先说说题目的意思和思路,再给出代码,最后分享出代码比较精髓的地方(有的话),另这随笔主要目的是方便自己以后使用,当然很是欢迎大家指出错误和批评。那就开始了:
题目说的是有一群小伙伴去旅游,某个人对某以特定的城市有一定的兴趣,并且多人一同去还有奖励,当然门票还是要的嘛。
输入:N(小伙伴的个数),M(城市的个数)紧接着的一行是各个城市的门票价格,然后是一个N*M的矩阵表示第i名游客对j城市的感兴趣度,最后还有一个N*N的矩阵表示若i旅客与j旅客结伴出行的话可以得到的奖金。特别注意的是游客可以中间插入,也可以中途离开,但若是中途离开以后就不得在后面的观光中出现.
输出:最后的结果若<=0,那么就输出"STAY HOME"。
思路还是状态压缩嘛,具体来讲就是用一个叫DP[i]的数组记录从开始的城市到当前城市中总共值中选出最大的,在用一个temp[i]数组来保存相应的DP[i]值,为后面的DP做准备,然后交替进行,期间必须保证离队以后的人不可以再次入队,这点怎么保证呢?
for(int i=1;i<=M;i++)
{
for(int j=0;j<bit;j++) dp[j]=MIN;
for(int j=0;j<bit;j++)
{
int weight=calculate(j,i);
for(int k=j;k<bit;k=(k+1)|j)
{
dp[j]=max(dp[j],temp[k]+weight);
}
}
for(int j=0;j<bit;j++) temp[j]=dp[j];
}
就用这几行代码就可以搞定了。第一个for循环是从1到M的城市,第二行是对于城市i的状态为j,第三行是总结出从1到i可以达到的最大值,中间的temp一直记录这些最大值,这与滚动数组非常类似。为什么这样写就能保证中途掉队的的在以后就一定加不进来呢?那么你可以仔细看看第三个for循环k是如何增加的(k=(k+1)|j)保证了变化后的k中一定包含有j的状态,也就是说参观第i-1座城市的人一定>=参观i城市时候的人,并且i城市的人在i-1城市的一定有,然后大家可以用类似于归纳法的思想来证明这种思路是正确的。
不罗嗦了,直接上代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 #define INFI (1<<10)+20 7 #define MIN -2139062144 8 int N,M; 9 int dp[INFI],bonus[12][12],cost[12]; 10 int inter[12][12],temp[INFI]; 11 12 inline int max(int a,int b) 13 { 14 if(a>b) return a; 15 return b; 16 } 17 int calculate(int k,int v) 18 { 19 int sieze[12],num=0,result=0; 20 for(int i=0;i<N;i++) if(k&(1<<i)) sieze[++num]=i; 21 for(int i=1;i<=num;i++) 22 { 23 result+=inter[sieze[i]][v]; 24 for(int j=i+1;j<=num;j++) result+=bonus[sieze[i]][sieze[j]]; 25 } 26 return result-num*cost[v]; 27 } 28 int DP() 29 { 30 int bit=1<<N; 31 for(int i=0;i<bit;i++) temp[i]=0; 32 for(int i=1;i<=M;i++) 33 { 34 for(int j=0;j<bit;j++) dp[j]=MIN; 35 for(int j=0;j<bit;j++) 36 { 37 int weight=calculate(j,i); 38 for(int k=j;k<bit;k=(k+1)|j) 39 { 40 dp[j]=max(dp[j],temp[k]+weight); 41 } 42 } 43 for(int j=0;j<bit;j++) temp[j]=dp[j]; 44 } 45 int result=-1; 46 for(int i=0;i<bit;i++) result=max(result,temp[i]); 47 if(result<=0) printf("STAY HOME.\n"); 48 else printf("%d\n",result); 49 } 50 int main() 51 { 52 53 while(scanf("%d %d",&N,&M),(N||M)) 54 { 55 for(int i=1;i<=M;i++) 56 scanf("%d",&cost[i]); 57 58 for(int i=0;i<N;i++) 59 for(int j=1;j<=M;j++) 60 scanf("%d",&inter[i][j]); 61 62 for(int i=0;i<N;i++) 63 for(int j=0;j<N;j++) 64 scanf("%d",&bonus[i][j]); 65 66 DP(); 67 } 68 69 return 0; 70 }