gym 101908 F(状压dp好题)
题意:
给你n个场景,每个场景有 k i ki ki场舞台剧,每一场舞台剧都有一个开始时间 s i si si,结束时间 e i ei ei,和一个获得的开心值 g i gi gi。一场舞台剧如果开始看了就要一直看到结束,如果这一场刚结束可以马上开始看另一个场景的另一场舞台剧(即点交集也还可以看),现在要你计算,如果每一个场景都至少要看一场舞台剧,则能获得的最大开心值是多少,如果不能做到就输出-1.
思路
显然状压DP
原问题:n个舞台都看过,时间到达第i个点
子问题:时间前i个点,看过的舞台的状态为j
转移时维护当前前缀最大DP值,转移到所有的当前时间作为左端点的区间即可,其实不离散化也能过
复杂度O(4000*(1<<n)),虽然我感觉常数除了个5都不止x
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn=1e4+5;
typedef long long ll;
int dp[3000][(1<<10)+5],n,m;
struct Node{
int id,l,r,val;
}node[1005];
vector<Node>s[3000];
int sum[maxn],cnt,num;
int ask(int x){
return lower_bound(sum+1,sum+1+cnt,x)-sum;
}
int main(){
scanf("%d",&n);
memset(dp,-1,sizeof(dp));
for(int i=0;i<n;++i){
scanf("%d",&m);
for(int j=1;j<=m;++j){
++num;
scanf("%d%d%d",&node[num].l,&node[num].r,&node[num].val);
node[num].id=i;
sum[++cnt]=node[num].l;
sum[++cnt]=node[num].r;
}
}
sort(sum+1,sum+1+cnt);
cnt=unique(sum+1,sum+1+cnt)-(sum+1);
for(int i=1;i<=num;++i){
node[i].l=ask(node[i].l);
node[i].r=ask(node[i].r);
s[node[i].l].pb({node[i].id,node[i].l,node[i].r,node[i].val});
}
dp[0][0]=0;
for(int i=1;i<=cnt;++i){
for(int k=0;k<(1<<n);++k){
dp[i][k]=max(dp[i-1][k],dp[i][k]);
}
for(int j=0;j<s[i].size();++j){
for(int k=0;k<(1<<n);++k){
if(dp[i][k]==-1)continue;
Node st=s[i][j];
dp[st.r][k|(1<<st.id)]=max(dp[st.r][k|(1<<st.id)],dp[i][k]+st.val);
}
}
}
cout<<dp[cnt][(1<<n)-1]<<"\n";
}