题目:https://nanti.jisuanke.com/t/A1951

题意:给你n个项目,每个项目有两个属性,a,b,如果第t个来完成第i个项目,那么价值就要加 t*a+b,每个项目还说了自己的先修项目,项目可以不做完,然后求出最大值

思路:首先n只有20,我们又要考虑不同的选择情况下的最大值,很明显是个状压DP,我们dp[n],代表n状态下的最大值,这里我们枚举每个状态的时候,我们枚举当前二进制位1,代表当前选择这个结尾,然后前面的状态+当前来取最大值,故状态其实就是选择这几件物品的最大值是多少,我们判断以当前结尾的时候,我们还要判断当前状态是否都有自己的先修项目,如果有才可以,然后还要考虑当前状态是否可用的问题,每当我们有状态更新的时候就标记当前状态可以用作递推后面来使用

 

#include<bits/stdc++.h>
#define maxn 25
#define mod 1000000007
using namespace std;
typedef long long ll;
struct sss{
    ll x,y,num;
    ll b[maxn];
}a[maxn];
ll dp[1<<20];
int vis[1<<20];
int main(){
    ll n;
    scanf("%lld",&n);
    for(int i=0;i<n;i++){
        scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].num);
        for(int j=0;j<a[i].num;j++){
            scanf("%lld",&a[i].b[j]);
        }
    }
    /*for(int i=1;i<(1<<n);i++){
        dp[i]=-mod*20;
    }*/
    ll mx=0; 
    vis[0]=1;
    for(int i=1;i<(1<<n);i++){
        for(int j=0;j<n;j++){
            if((i>>j)&1){//判断是否有当前项目,如果有就假设以当前项目结尾
                int flag=0;
                for(int k=0;k<a[j].num;k++){//枚举先修项目
                    if((i>>(a[j].b[k]-1))&1) continue;
                    flag=1;
                    break; 
                } 
                int sl=0;
                ll z=i;
                while(z){
                    sl+=z%2;
                    z/=2;
                }
                if(flag==0&&vis[i^(1<<j)]){
                    vis[i]=1;
                    dp[i]=max(dp[i],dp[i^(1<<j)]+sl*a[j].x+a[j].y);
                    mx=max(mx,dp[i]);
                    //if(dp[i]==75)cout<<i<<endl;
                }
            } 
        }
    }
    //cout<<dp[1]<<"\n";
    cout<<mx;
}