题意 : 一个人要完成m道题目, 他不会,只能去求助她的n个朋友,每个朋友 可以完成mi道题目,需要报酬xi,(拿到报酬后把他会的题目可以全部完成)。
同时 每个朋友 去做题目 需要这个人的电脑 有ki个monitor, monitor价值都为b。(即最终要有monitor的个数为 她选择的朋友中的ki的最大值。)
如何选择 使得 总的花费最少,做完所有的题目。
m很小 (20), 想到了状态压缩dp, 没有monitor这个东西的话,就是很裸的 状态压缩dp。
我们可以 把n个朋友按照,所需monitor的数量从大到小排序,
dp[i][j]表示从1~ith朋友中选择 达到j这个状态,最小的花费(不包括monitor),dp[i][j]=min(dp[i][j],dp[i-1][ k| sta[t]]+x[t]) 最终结果为ans=min(ans,dp[i][(1<<m)-1]+b*k[i]);
这样表示的话 会MLE,考虑到每个状态dp[i][]只会和dp[i-1][]有关, 所以可以用滚动数组 就可以A掉了。
进一步优化的话,可以搞成一维,类似背包,状态转移是逆序遍历就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define INF 2e18
typedef __int64 LL;
#define N 105
#define M 20
LL n,m,b,dp[1<<M];
struct node
{
LL x,k,m,save;
}num[N];
int cmp(node x,node y)
{
return x.k<y.k;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen ("in.txt" , "r" , stdin);
#endif
scanf("%I64d%I64d%I64d",&n,&m,&b);
LL tmp;
for(int i=1;i<=n;i++)
{
scanf("%I64d%I64d%I64d",&num[i].x,&num[i].k,&num[i].m);
num[i].save=0;
for(int j=1;j<=num[i].m;j++)
{
scanf("%I64d",&tmp); num[i].save|=(1<<(tmp-1));
}
}
sort(num+1,num+1+n,cmp);
for(int i=1;i<(1<<m);i++) dp[i]=INF;
dp[0]=0;
LL ans=INF;
for(int i=1;i<=n;i++)
{
for(int j=(1<<m)-1;j>=0;j--)
{
int next=j|num[i].save;
dp[next]=min(dp[next],dp[j]+num[i].x);
}
ans=min(ans,dp[(1<<m)-1]+num[i].k*b);
}
if(ans>=INF) printf("-1\n");
else printf("%I64d\n",ans);
return 0;
}