CF417D
题意
- 一共需要解决m道问题。小伙伴至少需要y台监视器才答应写题目,每台b元。每个人又需要x元的费用。给出每个人可以解决的题目。最少需要多少花费能够解决所有问题。
题解
- 状态压缩DP。把每个人可以解决的题目给状态压缩一下。
- 按监视器数量对大家排序。枚举到第i个人,那么编号≤i的人都满足监视器的要求。监视器的费用可以单独考虑。不用dp。
- dp[i]表示能够解决i道题目的最小花费。初始化为-1,dp[0] = 0。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll const inf = 4e18;
int const N = 105;
int const M = 20;
int n,m,b;
struct Node
{
ll x,y;
int s;
bool operator < (const Node& e)const{
return y < e.y;
}
}p[N];
ll dp[(1<<M)+10]; //dp[i]表示解决i道题目的最小花费
ll solve(){
ll ans = inf;
memset(dp,-1,sizeof(dp));
dp[0] = 0;
for(int i=0;i<n;i++){
for(int j=0;j<(1<<m);j++){
if(dp[j] == -1) continue;
if(dp[j|p[i].s] == -1){
dp[j|p[i].s] = dp[j] + p[i].x;
}else dp[j|p[i].s] = min(dp[j|p[i].s],dp[j] + p[i].x);
}
if(dp[(1<<m)-1] != -1)
ans = min(ans,dp[(1<<m)-1] + p[i].y * b);
} //如果第i个人参与,且价值比上一次还大,那么取上一次。如果上一次小,取这一次。如果没参与,那么还是上一次的。
return ans == inf ? -1 : ans;
}
int main(){
scanf("%d%d%d",&n,&m,&b);
for(int i=0;i<n;i++){
int pro;
scanf("%lld%lld%d",&p[i].x,&p[i].y,&pro); //x的雇佣金,y台显示器,能够解决pro个问题
for(int j=1;j<=pro;j++){
int k; scanf("%d",&k);
p[i].s |= 1 << (m - k);
}
}
sort(p,p+n);
printf("%lld",solve());
return 0;
}