Codeforces 417D Cunning Gena【排序+状压dp】

D. Cunning Gena
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

A boy named Gena really wants to get to the "Russian Code Cup" finals, or at least get a t-shirt. But the offered problems are too complex, so he made an arrangement with his n friends that they will solve the problems for him.

The participants are offered m problems on the contest. For each friend, Gena knows what problems he can solve. But Gena's friends won't agree to help Gena for nothing: the i-th friend asks Gena xi rubles for his help in solving all the problems he can. Also, the friend agreed to write a code for Gena only if Gena's computer is connected to at least ki monitors, each monitor costs b rubles.

Gena is careful with money, so he wants to spend as little money as possible to solve all the problems. Help Gena, tell him how to spend the smallest possible amount of money. Initially, there's no monitors connected to Gena's computer.

Input

The first line contains three integers nm and b (1 ≤ n ≤ 1001 ≤ m ≤ 201 ≤ b ≤ 109) — the number of Gena's friends, the number of problems and the cost of a single monitor.

The following 2n lines describe the friends. Lines number 2i and (2i + 1) contain the information about the i-th friend. The 2i-th line contains three integers xiki and mi (1 ≤ xi ≤ 1091 ≤ ki ≤ 1091 ≤ mi ≤ m) — the desired amount of money, monitors and the number of problems the friend can solve. The (2i + 1)-th line contains mi distinct positive integers — the numbers of problems that the i-th friend can solve. The problems are numbered from 1 to m.

Output

Print the minimum amount of money Gena needs to spend to solve all the problems. Or print -1, if this cannot be achieved.

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个问题,有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);
    }
}











  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值