dp训练第9题 codeforces580 D.Kefa and Dishes dp-状压dp

给定物品总数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');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值