一共有M个问题,有N个小伙伴可以帮你解决问题,其中一个监视器的金额为b。
对应接下来N组输入,对应每组输入两行,对应表示第i个小伙伴的信息,其中第一行包含三个元素,第一个元素表示雇佣这个小伙伴帮忙解决问题的花费,第二个表示这个小伙伴帮忙解决问题所需要的监视器的个数,第三个元素表示这个小伙伴能够解决的问题数。
(假如第一个小伙伴需要1个监视器,第二个小伙伴需要两个监视器,那么一共建立起来两个监视器就能同时满足两个条件)
接下来一行,表示能够解决问题的编号。
对应解决M个问题,求一个最小花费方案。
思路:
1、首先不考虑这个监视器的问题:
①对应观察到M只有20,那么我们可以对应设定dp【i】表示雇佣小伙伴解决了状态i时候的这些问题的最小花费。其中i=5.对应二进制表示:101,那么我们就能够表示5这个状态能够解决第一个和第三个问题,但是现在没有解决第二个问题。
②那么不难想到状态转移方程:dp【q】=min(dp【q】,dp【j】+a【i】.val)(其中对应第i个人能够解决状态q比状态j多出来的问题)
③对应解决上式状态转移方程,我们设定tmp【i】表示第i个人能够解决问题的状态,(例如tmp【1】=5,表示第1个人能够解决第一个和第三个问题,但是解决不了第二个问题。)那么我们此时第一层for枚举人,从0到n,然后第二层for枚举状态j,然后判断一下状态j下,有没有第i个人能够解决的问题并且状态j没有解决完的问题,对应判断:if(tmp【i】&j==tmp【i】)那么说明状态j已经解决了所有第i个人能够解决的问题,否则相反,并且我们同时设定q=j|a【i】;那么,dp【q】=min(dp【q】,dp【j】+a【i】.val);
此时状态转移过程最终结果dp【(1<<m)-1】就是解决了所有问题m所需要的最小雇佣花费。
2、那么此时考虑这个监视器问题;
①我们可以二分监视器个数,但是时间复杂度略高,可能会T
②我们其实直接将N个人按照需要监视器的个数从小到大排序即可,那么对应在将每个人都状态转移完毕之后,我们维护一次最小值:
ans=min(ans,dp【(1<<m)-1】+a【i】.yi*b),其中a【i】.yi表示当前这个人需要的监控器个数、这样保证前i个人进行了状态转移之后,当前这第i个人就是需要监控器个数最多的人,那么对应在雇佣的最小花费的基础上,维护一个最小总花费即可。
3、注意数组大小,注意数据范围,注意dp数组初始化大小,需要long long int的数据的地方不要忘记即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll __int64
#define inf 2000000000000000000
struct node
{
int xi;
ll yi,pos;
}a[1515];
int n,m;
ll b;
int tmp[151515];
ll dp[(1<<20)+15];
int cmp(node a,node b)
{
return a.yi<b.yi;
}
int main()
{
while(~scanf("%d%d%I64d",&n,&m,&b))
{
for(int i=0;i<(1<<m);i++)dp[i]=inf;
memset(tmp,0,sizeof(tmp));
for(int i=0;i<n;i++)
{
int k;
scanf("%d%I64d%d",&a[i].xi,&a[i].yi,&k);
while(k--)
{
int x;
scanf("%d",&x);
x--;
tmp[i]+=(1<<x);
}
a[i].pos=i;
}
sort(a,a+n,cmp);
dp[0]=0;
ll ans=inf;
for(int i=0;i<n;i++)
{
for(int j=0;j<(1<<m);j++)
{
int pos=a[i].pos;
if((j&tmp[pos])!=tmp[pos])
{
int q=j|tmp[pos];
dp[q]=min(dp[q],dp[j]+a[i].xi);
}
}
if(dp[(1<<m)-1]!=inf)
{
ans=min(dp[(1<<m)-1]+a[i].yi*b,ans);
}
}
if(ans==inf)printf("-1\n");
else
printf("%I64d\n",ans);
}
}