题目链接:https://nanti.jisuanke.com/t/30994
题意:有n道题目,每道题目有两个值,a,b。若 你在 t 分钟时做了某道题,那么你会获得 a * t + b的分数。但是做某道题时,有限制条件,你想要先做完了另外一些题目才能做这道题。比如 在做第三题时,第三题要求必须先做完第一题和 第二题,那么你要想做第三题 必须先做完第一题和第二题,问如何做题才能最后分数最大。
题解:一看就是一道状压dp,但是场上没想出来,没想到状态中1的个数就是当前时间这一点,导致没写出来。
dp【i】:表示当前状态的最大得分,i为0|1的二进制数,0表示没做,1表示做了。
c【i】:表示第i道题的限制条件,用0|1表示
那么就能理解为:若某个状态 state 中 第 i 题做了 ,那么 他一定是有 state^(1<<i)这个状态转移过来的。遍历所有状态 然后 取出最大值即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mo = 1e9+7;
const int maxn = 1 << 20;
const ll inf = 0x3f3f3f3f;
int n,m,k,T;
ll dp[maxn],ans,tmp,sum;
ll a[25],c[25],b[25];
ll fcount (int x){
ll s = 0 ;
while(x){
s ++;
x &= (x-1);
}
return s;
}
int main(){
while(scanf("%d",&n) != EOF){
ans = 0 ;
for(int i = 0 ; i < n ; i ++){
scanf("%lld%lld%lld",&a[i],&b[i],&k);
c[i] = 0;
for(int j = 0 ; j < k ; j ++){
int x;
scanf("%d",&x);
x --; // 我的题目 编号从 0 开始 ,故需要-1
c[i] |= (1<< x);
}
}
m = 1 << n;
for(int i = 0 ; i < m ; i ++)
dp[i] = -inf;
dp[0] = 0; // 最开始状态 的得分一定 为 0
for(int i = 1 ; i < m ; i ++){
for(int j = 0 ; j < n ; j ++){
if(i & (1<< j)){ // 若 i 题 做了
int tmp = i ^ (1<<j); // tmp 为 i 没做的状态
if(dp[tmp] == -inf) continue; // 若没有状态 能到达 tmp 就证明 不合法 跳过
if((tmp|c[j]) == tmp){ // 判断 tmp这个 状态 是否 能满足 做 i 题的 条件
ll t = fcount(i); // 计算 i 状态 的 时间
dp[i] = max(dp[i],dp[tmp] + a[j] * t + b[j]); // 转移
ans = max(ans,dp[i]); // 维护最大值。
}
}
}
}
printf("%lld\n",ans);
}
return 0;
}