题意:一共n道菜,吃m道,有k个规则,每个菜有自己的价值,每个规则说明吃完X接着吃Y可以额外获得Z个价值。
问可以获得的最大价值是多少。
思路:1<<18保存所有状态,第二维保存最后吃的哪道菜,然后从吃的菜里选一道,从没吃的菜里选一道,吃完X吃Y,
更新dp,判断一下是否当前吃了m道,更新ans便可,记忆化搜索也可以。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int Max=100005;
const int INF=0x3f3f3f3f;
long long dp[(1<<18)+10][20];
int val[20],add[20][20];
int n,m,k;
int main()
{
int x,y,value,tot,cnt;
long long ans;
while (scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(dp, 0, sizeof(dp));
memset(add, 0, sizeof(add));
for (int i=0; i<n; i++)
scanf("%d",&val[i]);
for (int i=0; i<k; i++)
{
scanf("%d%d%d",&x,&y,&value);
add[x-1][y-1]=value;
}
for (int i=0; i<n; i++)
dp[1<<i][i]=val[i];
tot=1<<n;
ans=0;
for (int i=0; i<tot; i++) //所有状态
{
cnt=0;
for (int j=0; j<n; j++) //已选了第j道
{
if (i& (1<<j))
{
cnt++;
for (int k=0; k<n; k++) //选第k道
{
if ((i& (1<<k))==0)
dp[i | (1<<k)][k]=max(dp[i | (1<<k)][k],dp[i][j]+val[k]+add[j][k]);
}
}
}
if (cnt==m)
{
for (int j=0; j<n; j++)
{
if (i& (1<<j))
ans=max(ans,dp[i][j]);
}
}
}
printf("%lld\n",ans);
}
return 0;
}
记忆化:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int Max=100005;
const int INF=0x3f3f3f3f;
long long dp[(1<<18)+10][20];
int val[20],add[20][20];
int n,m,k;
long long dfs(int x,int y,int z)
{
if (dp[x][y]!=-1)
return dp[x][y];
dp[x][y]=0;
if (z==m)
return dp[x][y];
for (int i=0; i<n; i++)
{
if (((1 << i) & x) ==0) //当前未选择第i道菜
dp[x][y]=max(dp[x][y],dfs(((1<<i) | x), i, z+1)+val[i]+add[y][i]);
}
return dp[x][y];
}
int main()
{
int x,y,value;
while (scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(dp, -1, sizeof(dp));
memset(add, 0, sizeof(add));
for (int i=0; i<n; i++)
scanf("%d",&val[i]);
for (int i=0; i<k; i++)
{
scanf("%d%d%d",&x,&y,&value);
add[x-1][y-1]=value;
}
printf("%lld\n",dfs(0,n+1,0));
}
return 0;
}