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