题意
展览会租大厅,一共8间展厅备选,有 R <=12 个需求需要满足,每个需求有k个备选大厅,需要在[a,b]时间区间内有且只有1个展厅全程服务(1<=a<=1000)。
思路
DP!!!
想法:
把每个需求分成两个点,一个为占用大厅(标号为偶数),一个为释放大厅(奇数),对需求的时间排序,时间相同时占用优先于释放,共至多12*2个节点。然后按照这个顺序,对每个节点(即时间点),都可能有一批状态s为有效的,这个状态为占用情况,用二进制来表示,0< s < (1<<8),dp[mark][s] = 1 表示到该step,s这种占用情况是可以达到的,这里mark是为了节省空间,做的循环dp。
状态方程:
我们对每个时间节点,遍历之前有效(dp[mark][s]=1)的状态,再分当前是需要占用还是释放两种情况考虑。
//伪代码
for every timepoint id
for every s that is available
for every h that is available hall(0~7)
if(id is even) //it's a request
dp[!mark][s|(1<<h)] = (s&(1<<h)) ? itself : 1
else //it's a release
dp[!mark][s^(1<<h)] = (s&(1<<h)) ? 1 : itself
初始化:
对每一步,都需要把当前需要的dp[mark]清零,如果不能由上一步推得,则不可能出现这一步有效的情况。
对第一步,只有所有大厅都没有被占用是有效的,即 s=0。
证明:
有个疑问需要证明,即在释放的时候,可能对于某状态,可释放的有多个,因为没有记录当初哪个需求当时选择了哪个大厅,故释放可能会有差错。所以当时做的时候还不敢用DP去想,而是想的dfs回溯,后来看到discuss有人这么提才敢继续想下去。
那么我们来考虑一下这个疑问。对于释放时两者都可释放且都被占用,这种情形可以分为两种情况,其一为两个时间段为包含关系,其二为交错关系。唔….怎么不对!反例出现了!
反例:
1
4
1 2 1 3
3 6 2 1 3
7 8 1 1
1 8 2 1 3
应该是NO, dp出来是YES, 是在释放的时候没有原则造成的。
心得:
对于DP,最关键的,是要勇敢。首先有个自以为不太切实际的想法,然后去推导可行性,然后再是转化关系与复杂度的演算。
虽然最后还是不太对的…….有时间做一下另外的解法。
代码
//能过但是原理有欠缺
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 12;
const int maxs = 1<<8;
struct R{
int id, pls;
}rs[maxn<<1];//for sort,id even:request,id odd:release
vector <int> req[maxn<<1];//the candidates
bool dp[2][maxs];
void ini(){
for(int i = 0; i < maxn; i++){
req[i].clear();
}
}
bool cmp(R a,R b){
if(a.pls == b.pls){
return (a.id & 1) < (b.id & 1);
}
return a.pls < b.pls;
}
void ins(int id, int num){
rs[id].id = id;
rs[id].pls = num;
}
void solve(int m){
int ok = 0,mark = 0;
int sise,tmp;
sort(rs,rs+m*2,cmp);
memset(dp[mark],0,sizeof dp[mark]);
dp[mark][0] = 1;
for(int i = 0; i < m*2; i++){
int id = rs[i].id;
int tid = id/2;
sise = req[tid].size();
memset(dp[!mark],0,sizeof dp[!mark]);
if(id%2==0){//request
for(int s = 0; s < maxs; s++){
if(dp[mark][s]){
for(int j = 0; j < sise; j++){
tmp = 1 << req[tid][j];
if(!(s&tmp)){
dp[!mark][s|tmp] = 1;
}
}
}
}
}
else{
for(int s = 0; s < maxs; s++){
if(dp[mark][s]){
for(int j = 0; j < sise; j++){
tmp = 1 << req[tid][j];
if(s&tmp){
dp[!mark][s^tmp] = 1;
}
}
}
}
}
mark = !mark;
}
mark = !mark;
for(int i = 0; i < maxs; i++){
if(dp[mark][i]){
ok = 1;
break;
}
}
if(ok) printf("YES\n");
else printf("NO\n");
}
int main(){
//freopen("in.txt","r",stdin);
int t,m,k,a,b,tmp;
cin >> t;
while(t--){
ini();
scanf("%d",&m);
for(int i = 0; i < m; i ++){
scanf("%d%d%d",&a,&b,&k);
ins(i*2,a);
ins(i*2+1,b);
for(int j = 0; j < k; j++){
scanf("%d",&tmp);
req[i].push_back(tmp-1);
}
}
solve(m);
}
return 0;
}