给定物品总数n(18),需要物品数m(n),规则数k(n*n),然后给定每个物品的满意度ai(1e9),和k个规则,每个规则包括三个数x,y,c(1e9)表示如果取完x物品立刻取y物品,则满意度提高c.
每个物品只能取一次,问取m个物品最高满意为多少.
这道题是syl ppt里状压dp的例题,看ppt时不小心看到了状态表示的方法,很好的一道题.
考虑到n最高只有18,所以使用状态压缩-二进制枚举方法,
但是存在连取加分的规则,所以额外记录一个状态:上一个已经取的物品.
注意将1到n的物品编号改为0到n-1
规则拿一个邻接矩阵存就好.
状态表示:dp[i][j]:状态为i,上一个取j时的最高满意度
结果表示:物品数恰为m时的各种状态的最大值.
取值范围:i从0到(1<<n)-1,j从0到n-1.
注意一些非法状态:物品数超过m,已经取的物品不含j.
边界条件:仅取了一件物品的状态.
状态转移:(递推)dp[i][j],设可取的菜k,下一个状态为dp[i+(1<<k)][k]=max(dp[i][j]+rule[i][j]);
状态的各种情况都要考虑好,状压的常用操作就是mask&(1<<j)了
dp最重要的就是状态,还有一道例题,晚上回来做,做完写总结.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read();
inline void write(int x);
const int M = 18;
int save[M],rule[M][M];
ll dp[1<<M][M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
//std::cin.sync_with_stdio(false);
int n=read(),m=read(),k=read();
for(int i=0;i<n;i++)
save[i]=read();
for(int i=0;i<k;i++)
{
int x=read(),y=read(),c=read();
rule[x-1][y-1]=c;
}
for(int id=0;id<n;id++) //边界
dp[1<<id][id]=save[id];
int mx=1<<n;
ll ans=0;
for(int mask=0;mask<mx;mask++)
{
int cnt=0; //记数
for(int id=0;id<n;id++)
if(mask&(1<<id)) cnt++;
if(cnt==m)
for(int j=0;j<n;j++)
ans=max(ans,dp[mask][j]);
else if(cnt>m) continue;
for(int j=0;j<n;j++)
{
if(mask&(1<<j))
for(int id=0;id<n;id++)
{
if(mask&(1<<id)) continue;
dp[mask|(1<<id)][id]=max(dp[mask|(1<<id)][id],dp[mask][j]+rule[j][id]+save[id]);
}
}
}
cout << ans << endl;
return 0;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}