Codeforces 417D.Cunning Gena (状压DP)

原题链接
小图非常想参加“俄罗斯密码杯”的决赛,或者至少能拿到一件 T-shirt 衫。但是比赛的题目太复杂了,所以他和 n 个朋友商量,帮他解决问题。比赛一共有 m 道题。小图知道每个朋友能解决的题目。但是他的朋友不会提供无偿帮助:第 i 个朋友要求小图支付 xi 元钱。另外还要求小图的电脑至少要连接 ki 个显示器,每个显示器的价格是 b 元钱。

小图对钱很谨慎,所以他想花尽可能少的钱来解决所有的问题。请你帮一帮小图,告诉他最少花多少元钱能解决所有题目。注意,一开始没有显示器连接到小图的电脑。

Input
第一行输入三个整数 n, m, b (1 ≤ n ≤ 100; 1 ≤ m ≤ 20; 1 ≤ b ≤ 109),分别表示小图的朋友数量,问题的数量和一台显示器的成本。

接下来输入的 2n 行数据描述了这些朋友各自的要求。第 2i 和 (2i + 1) 行包含关于第 i 个朋友的信息。第 2i 行包含三个整数 xi, ki 和 mi (1 ≤ xi ≤ 109; 1 ≤ ki ≤ 109; 1 ≤ mi ≤ m),分别表示该朋友想要的钱数,监视器个数和他可以解决的问题的数量。第 (2i + 1) 行包含 mi 个不同的正整数,表示第 i 个朋友可以解决的问题的编号。问题的编号从 1 到 m。

Output
输出小图解决所有题目要花费的最少的钱。

如果没有满足条件的结果,请输出 -1。

Examples
Input
2 2 1
100 1 1
2
100 2 1
1
Output
202
Input
3 2 5
100 1 1
1
100 1 1
2
200 1 2
1 2
Output
205
Input
1 2 1
1 1 1
1
Output
-1

思路:
m的范围很小,考虑用二进制来表示某个问题是否被解决.
dp[i]表示状态为i时不包括显示器费用的最小花费,这样只需要枚举每个朋友可以转移哪些状态即可。
对每个朋友所需的显示器数量进行排序,这样枚举到第i个人时,所需的显示器费用是固定的。
爆ll了,要开ull.
代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
#define I_int ll
inline ll read()
{
    ll 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;
}
#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
ll ksm(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
#define PI acos(-1)
#define x first
#define y second
const int maxn=1e6+7,inf=0x3f3f3f3f;
struct node{
    ull x,k,m;
    ull s;
}a[110];
bool cmp(node a,node b){
    return a.k<b.k;///排序后枚举到第i个所需的显示器数量就是总的数量
}
ull dp[1<<21];///dp[i]表示当前状态是i时,所需要的最小花费(不计算显示器的花费)
int main()
{
    int n=read,m=read,b=read;
    for(int i=1;i<=n;i++){
        cin>>a[i].x>>a[i].k>>a[i].m;
        for(int j=0;j<a[i].m;j++){
            ull t;cin>>t;
            a[i].s|=1<<t-1;///记录每个人能够解决的问题数
        }
    }
    sort(a+1,a+1+n,cmp);
    dp[0]=0;
    for(int i=1;i<(1<<m);i++) dp[i]=1e19;
    ull res=1e19;
    for(int i=1;i<=n;i++){
        for(int j=0;j<(1<<m);j++)
            dp[j|a[i].s]=min(dp[j|a[i].s],dp[j]+a[i].x);
        res=min(res,dp[(1<<m)-1]+a[i].k*b);
    }
    if(res==1e19) puts("-1");
    else cout<<res<<endl;
    return 0;
}



posted @ 2021-03-04 19:35  OvO1  阅读(55)  评论(0编辑  收藏  举报