题目链接:传送门
1. 题意:你要让n个朋友做完m道题,每个人能做掉特定某些题目,有一个花费x和需要的monitor(班长)个数,每个monitor(监视器)需要一定花费,但是monitor(显示屏)可以给所有朋友公用。求做完所有题的最小花费。(e.i. m很小)
2. 思路:m很小,所以压缩m的所有状态。f[state]表示做了state状态所花费的最小费用(不买monitor)。然而monitor还是需要管的,所以把所有朋友按monitor数量升序排序,这样每次用一个朋友更新状态时他自己必然是需要monitor最多的,那就拿他自己的monitor数量更新就好了。
3. 实现:排序+状压DP
4. 曾经的错误思路:monitor数量降序排列,然后f[state]表示加上monitor之后的最小价值,g[state]表示对应的f[state]状态去掉monitor花费后的价格。因为降序,所以所有f[nxt]都是由已经拥有比他大的monitor数量的状态转移过来,所以只要再加上买朋友的花费就好了。然而这样的思路不能保证记录的f[]最优,因为有monitor的影响,如果有monitor较少但f值较大的,他不会被更新,这是错误的。
5. 最后贴代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int N = 101;
const int E = (1<<20);
int n, m, b, maxst;
struct NODE{
int x, k, st;
}a[N];
int f[E], ans;
bool cmp(NODE x, NODE y){return x.k < y.k;}
main()
{
scanf("%lld%lld%lld", &n, &m, &b);
maxst = 1<<m;
for (int i = 1; i <= n; i++){
int num, y;
a[i].st = 0;
scanf("%lld%lld%lld", &a[i].x, &a[i].k, &num);
for (int j = 1; j <= num; j++){
scanf("%lld", &y);
a[i].st += 1<<(y-1);
}
}
sort(a+1, a+n+1, cmp);
memset(f, -1, sizeof(f));
f[0] = 0;
ans = -1;
for (int i = 1; i <= n; i++)
for (int now = 0; now < maxst; now++)
if (f[now] != -1){
int nxt = now|a[i].st;
if (f[nxt] == -1 || f[nxt] > f[now]+a[i].x){
f[nxt] = f[now]+a[i].x;
if (nxt == maxst-1 && (ans == -1 || ans > f[nxt]+a[i].k*b))
ans = f[nxt]+a[i].k*b;
}
}
printf("%lld", ans);
return 0;
}