版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/sdau20163942/article/details/82319695
题解来自IGVA大佬:https://blog.csdn.net/LSD20164388/article/details/82313223
题目链接:https://nanti.jisuanke.com/t/30994
题意:有n个问题编号为1~n,每个问题给出ai,bi,si,以及pi1,pi2,pi3...pij...pisi,1<=j<=si,分别代表第t分钟解决这个问题可以得到t×ai+bi的分数,si代表课i有si门前导前导分别为pi1...pisi,只有前导问题全部解决当前问题才能解决;现在每一分钟可以解决1个问题,得到相应的价值(价值可能为负),可以在任意时间放弃答题,问最大得到多少分。
解析:注意n的范围是0<n<=20,那么往dfs以及状压dp想,发现是比较裸的状压dp,状压不是很熟悉,是队友A的,但代码很容易看懂。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<set>
#define ll long long
using namespace std;
const int mo=1e9+7;
const int maxn=1<<20;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int n,m,k,T;
ll dp[maxn],ans,tmp,sum;
ll a[25],c[25],b[25];
ll fcount(int x)//计算x的二进制里有几个1
{
ll s=0;
while(x){
s++;
x&=(x-1);
}
return s;
}
int main() {
int T;
while(scanf("%d",&n)!=EOF)
{
ans=0;sum=0;
for(int i=0;i<n;i++)
{
scanf("%lld%lld%d",&a[i],&b[i],&k);
c[i]=0;
for(int j=0;j<k;j++)
{
int x;
scanf("%d",&x);
x--;
c[i]|=(1<<x);//c[i]保存所有前导问题
}
}
m=1<<n;
for(int i=0;i<m;i++)//m就是所有状态数,即2^n
dp[i]=-inf;//数据水了,这里改成-1也能过,实际上-1不一定不合法
dp[0]=0;//一道题都没做
for(int i=1;i<m;i++)//枚举每种状态
{
for(int j=0;j<n;j++)//枚举判断问题j是否要解
if(i&(1<<j))//如果问题j要解
{
int tmp=i^(1<<j);//没选问题j的状态,即假设的之前选过的所有课
if(dp[tmp]==-inf) continue;
if((tmp|c[j])==tmp)//前导问题已经全部解决
{
ll t=fcount(i);//共解决了t个问题,即当前为第t分钟
dp[i]=max(dp[i],dp[tmp]+a[j]*t+b[j]);
ans=max(ans,dp[i]);
}
}
}
printf("%lld\n",ans);
}
return 0;
}