codeforces 580D. Kefa and Dishes(状压dp)

题目传送门

题目大意:有n种菜,你可以选择m种,但是还有k种规则或者说是条件,每一种菜都有一定的满意度,按顺序吃的话也会获得一定的满意度,要你求出最大的满意度是多少

比如第1的样例2种菜,每种菜的满意度都是1,还有一种规则就是按照吃完2号菜再吃一号菜就会获得值为1的满意度

因为只有两种状态吃 或者 不吃,因此可以用1 0表示其状态,又因为n<20,所以用状压DP可以的(我也想过用区间DP但是发现不好处理不相邻的情况),两种状态或许就是状压DP的标志吧  '>==<'

需要推出状态转移方程dp[k][j]=max(dp[k][j],dp[s][i]+dig[j]+m[i][j])要从第i种推到j种

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b)   for(int i=a;i<=b;i++)
const int MOD=2520;
ll vul[20][20];
ll dp[1<<18][20];//dp[i][j]表示在状态j(记录某个物品选或没选)下第i个物品作为最后一个选取的物品的最大值
ll dig[40];

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int n,m,k;
    cin>>n>>m>>k;
    rep(i,0,n-1)
    cin>>dig[i];
    rep(i,0,k-1)
    {
        int x,y;
        cin>>x>>y;
        x--,y--;
        cin>>vul[x][y];
    }
    int tot=1<<n;
    ll ans=0;
    int cnt=0;
    rep(i,0,n-1)//别忘了进行预处理一下,好像状压dp都是要预处理
    dp[1<<i][i]=dig[i];
    for(int s=0;s<tot;s++)//枚举所有的情况比如n=3,就从000~111,将全部的情况都列出
    {
        cnt=0;
        for(int i=0;i<n;i++)
            if((s&(1<<i))!=0)//在这里就是说明当情况为s时,哪些菜已经吃了(选了几盘),也就是为1的情况是哪几盘
            {//为下面的dp[s][i]做铺垫,说明上一种情况是i结尾的
                cnt++;
                for(int j=0;j<n;j++)//将下一次的情况加入到dp数组中
                    if((s&(1<<j))==0)//说明不与前一次选的冲突,因为已经选过的就不能再选了
                {
                    int k=s|(1<<j);//将符合此次循环的情况取并集加入到dp中,看通过第j盘菜可不可以从s的状态到将j盘选入
                    dp[k][j]=max(dp[k][j],dp[s][i]+dig[j]+vul[i][j]);//的s|(1<<j)的的情况变大(在s的状态第j种没有选)
                }
            }
        if(cnt==m)
        {
         for(int l=0;l<n;l++)
            if((s&(1<<l))!=0)
            ans=max(ans ,dp[s][l]);
        }
    }
        cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/81535398