CF580D Kefa and Dishes 【状压dp】By cellur925

题目传送门

友情链接:new2zydalao%%%  一篇优秀的状压文章


 

题目大意:$n$个菜有$k$个规则,如果kefa在吃完第$xi$个菜之后吃了第$yi$个菜(保证$xi$、$yi$不相等),

那么会额外获得$ci$ (0<=$ci$<=$10^9$)(0<=$ci$<=$10^9$)的满意度。(0<$n$<=18).但是他希望自己吃菜的顺序得到的满意度最大,

请你帮帮kefa吧!

 

数据范围这么小==,应该会用到状压的。但是之前状压都是那些比较明显的在棋盘上的类型,这种完全布吉岛如何设计状态及转移。

上次做的类似题是NOIp2017宝藏。其实感觉这种题有一个(较)固定的思路:设$f[i][j]$表示当前状态为$i$,目前在$j$。这次我们可以如出一辙,设$f[i][j]$表示为当前状态为$i$,最后吃的一道菜为$j$获得的最大满意度。

好啦。有了状态,转移就不难了。我们枚举当前状态以及当前吃的最后一个菜,再枚举吃的上一个菜,那么显然有方程$f[i][j]=$max(f[i^(1<<i)][k]+a[j]+w[j][k])$。注意一下细节及初值就行了,感觉本题并没有太难==。


 

$Code$

 1 #include<cstdio>
 2 #include<algorithm>
 3 
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 int n,m,qwq;
 8 ll ans,a[100],f[270000][20],w[100][100];
 9 
10 int count(int x)
11 {
12     int tmp=0;
13     while(x)
14         tmp+=(x&1),x>>=1;
15     return tmp;
16 }
17 
18 int main()
19 {
20     scanf("%d%d%d",&n,&m,&qwq);
21     for(int i=0;i<n;i++) scanf("%lld",&a[i]),f[(1<<i)][i]=a[i];
22     for(int i=1;i<=qwq;i++)
23     {
24         ll x=0,y=0;
25         scanf("%lld%lld",&x,&y);
26         scanf("%lld",&w[x-1][y-1]);
27     }
28     int fAKe=(1<<n)-1;
29     for(int i=0;i<=fAKe;i++)
30     {
31         int sum=count(i);
32         for(int j=0;j<n;j++)
33         {
34             if(!(i&(1<<j))) continue;
35             for(int k=0;k<n;k++)
36             {
37                 if(!(i&(1<<k))||k==j) continue;
38                 f[i][j]=max(f[i][j],f[i^(1<<j)][k]+w[k][j]+a[j]);
39             }
40             if(sum==m) ans=max(ans,f[i][j]);
41         }
42     }
43     printf("%lld",ans);
44     return 0;
45 }
View Code

本题中还运用到了一个小技巧(和sjtdalao学的)。位运算我们都知道从0开始搞,那么有时我们为了方便书写,把给出的元素从0~n-1进行标号,符合二进制的传统,就比较舒服。

posted @ 2018-10-01 23:10  cellur925&Chemist  阅读(194)  评论(0编辑  收藏  举报