Description
当前有n(n<=12)个工作,和8个工人。现在每个工作需要占用一个工人的从[a,b]这个区间的时间(一个工人自然不可能在同一个时间做2个不同的工作),且一个工作不一定是所有工人都能够完成的。现在给出每个工作的描述,问是否存在一种安排方案使得所有工作都能完成。
Input
有多组数据。第一行一个数tot表示数据的组数,后面紧接tot组数据。
对于每一组数据的第一行有一个整数n,表示工作的数目。后面n行每行描述一个工作。
对于一个工作,a,b,k,h1,h2……hk来描述,表示这个工作需要占用一个工人[a,b]的时间,并且能够完成这个工作的工人只有k个,标号分别是h1,h2……hk。
Output
对于每组输入数据,输出一行YES(如果可以安排一种方案使得工作完成)或者是NO(无法安排一种方案)
Sample Input
2
2
1 1 1 1
2 2 1 1
2
1 2 1 1
2 2 1 1
Sample Output
YES
NO
首先这是一道可行性判定问题。
我们注意到工作完成先后顺序是没有影响的,而且影响范围是一个区间,因此想到了拆分思想,也就是说将一个工作拆成开始和完成两个区间(对于只有两种状态的集合元素,这是一个套路),用工人作为状态进行递推。
#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int tot= (1<<8)-1;//工人状态总数
struct Work{int f,ti,id;}a[27];
int f[27][tot];//表示第i个工作开始(结束)时,工人做工情况
int n,nd[13][9];
bool cmp(const Work&A,const Work&b){
return (A.ti<b.ti)||(A.ti==b.ti&&A.f>b.f);
}
void init(){//拆分
memset(f,0,sizeof(f));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i*2-1].ti,&a[i*2].ti,&nd[i][0]);
a[i*2-1].f=1;a[i*2].f=0;
a[i*2-1].id=a[i*2].id=i;
for(int j=1;j<=nd[i][0];j++)scanf("%d",&nd[i][j]),--nd[i][j];
}
sort(a+1,a+1+n*2,cmp);
}
inline void dp(){
f[0][0]=1;
Inc(i,0,n*2-1)
Inc(j,0,tot)
Inc(k,1,nd[a[i+1].id][0])
if(a[i+1].f){//工作开始
if(!(j&(1<<nd[a[i+1].id][k])))continue;//工作开始时工人k不在
f[i+1][j]|=f[i][j^(1<<nd[a[i+1].id][k])];
}else {
if((j&(1<<nd[a[i+1].id][k])))continue;//工作结束时工人k还在做
f[i+1][j]|=f[i][j|(1<<nd[a[i+1].id][k])];
}
if(f[2*n][0])puts("YES");
else puts("NO");
}
int main(){
int T;scanf("%d",&T);
while(T--){
init();
dp();
}
return 0;
}