2 2 2 1 1 0 1 1 1 2 1 0 0 1 0 1 4 2 2 1 0 1 1 1 2 0 2 2 3 0 0 1 1 2 0 2 2 0 0 0 1 1 1 0 2 2 2 0 2 1 1 1 0
Case 1: 2 Case 2: 2HintIn the first sample, you must select all of roles, because one role couldn't beat the other role in any model. In the second sample, you can select role 0 with its model 0, and role 3 with its model 0, then role 1 and role 2 will to be defeated no matter which model they use.
有N个英雄,最多有两个状态,每个状态的英雄都能打败一些其他状态的英雄。问,至少选出几个英雄就能打败剩下的英雄的所有状态。
做法:
容易想到的是选出一些状态的英雄打败其他所有英雄,可以用重复覆盖的方法建模型。但是一个英雄只能选一个状态,所以不能是有限制的选择行的。因此还要添加限制条件。同一种英雄的不同状态,设置一个列,如果该列的对应的这个英雄的状态的行上为1.除了在每个状态的英雄能打败的状态的英雄上设置1外,一个英雄的状态能打败这个英雄的其他状态。然后搜索的时候 不对限制列的进行f()函数的判断,优化的时候也不在这些限制的列上选择。只有在选了一行后,才处理限制列,把对应的其他行给删除掉。
#include<iostream> #include<cstdio> #include<cstring> #include<math.h> #include<algorithm> #define maxn 100 #define maxm 1000 using namespace std; //十字链表指针(4) int up[maxm],down[maxm],lef[maxm],righ[maxm]; //行指针,记录在该列为1的行数,覆盖矩阵,行属性,列属性 int head[maxn],sum[maxn],row[maxm],colume[maxm]; bool v[maxn]; //行数,列数,选取行数上线,矩阵点编号 int maxmum,size; //初始化r行,c列 void prepare(int r,int c){ for(int i=0;i<=c;++i){ sum[i]=0; up[i]=down[i]=i; lef[i+1]=i; righ[i]=i+1; } righ[c]=0; size = c; memset(head,-1,sizeof(head)); } //移除某列 void remove(int c){ for(int i = down[c];i != c;i = down[i]) lef[righ[i]]=lef[i],righ[lef[i]]=righ[i]; } //插入某列 void resume(int c){ for(int i=up[c];i!=c;i=up[i]) lef[righ[i]]=righ[lef[i]]=i; } //删除列及其相应的行 void remove1(int c){ int i,j; lef[righ[c]] = lef[c]; righ[lef[c]] = righ[c]; for(i = down[c]; i != c; i = down[i]){ for(j = righ[i]; j != i; j = righ[j]){ up[down[j]] = up[j]; down[up[j]] = down[j]; sum[colume[j]]--; } } } //恢复列及其相应的行 void resume1(int c){ int i,j; righ[lef[c]] = c; lef[righ[c]] = c; for(i = down[c]; i != c; i = down[i]){ for(j = righ[i]; j != i; j = righ[j]){ up[down[j]] = j; down[up[j]] = j; sum[colume[j]]++; } } } int flag; //代价函数 int f(){ int i,j,c,ret=0; for(c = righ[0];c;c = righ[c])v[c]=1; for(c = righ[0];c;c = righ[c]) if(v[c] && c <= flag) for(++ret,v[c]=0,i=down[c];i!=c;i=down[i]){ for(j=righ[i];j!=i;j=righ[j])v[colume[j]]=0; } return ret; } //执行第k层 bool Dance(int k){ if(k+f()>= maxmum)return 0; //righ[0]=0表示覆盖完毕 if(!righ[0] || righ[0] > flag) { //cout<<k<<" k"<<endl; maxmum = k; return 1; } int i,j,c,tmp=maxm; for(i=righ[0];i <= flag;i=righ[i]) if(sum[i]<tmp)tmp=sum[c=i]; for(i=down[c];i!=c;i=down[i]){ remove(i); for(j=righ[i];j!=i;j=righ[j]) if(colume[j] <= flag) remove(j); for(j=righ[i];j!=i;j=righ[j]) if(colume[j] > flag) remove1(colume[j]); Dance(k+1); for(j=lef[i];j!=i;j=lef[j]) if(colume[j] > flag) resume1(colume[j]); for(j=lef[i];j!=i;j=lef[j]) if(colume[j] <= flag) resume(j); resume(i); } return 0; } //插入r行c列 void Link(int r,int c){ ++sum[colume[++size] = c]; down[size] = down[c]; up[down[c]] = size; up[size] = c; down[c] = size; row[size] = r; if(head[r] < 0)head[r] = lef[size] = righ[size] = size; else{ righ[size] = righ[head[r]]; lef[righ[head[r]]] = size; lef[size] = head[r]; righ[head[r]] = size; } } int ma[100][200]; int ty[50]; int su[50]; int kil[50]; int rol[50][50][2]; int main(){ int t,n,m; scanf("%d",&t); for(int tt = 1; tt<= t;tt++){ scanf("%d",&n); memset(ma,0,sizeof(ma)); int cnt = 0; for(int i = 0;i < n; i++){ scanf("%d",&ty[i]); for(int l = 0;l < ty[i]; l++){ scanf("%d",&kil[cnt]); for(int j = 0;j < kil[cnt];j++) scanf("%d%d",&rol[cnt][j][0],&rol[cnt][j][1]); cnt++; } } su[0] = ty[0]; for(int i = 1; i < n; i++) su[i] = su[i-1] + ty[i]; for(int i = 0;i < cnt; i++){ for(int j = 0;j < kil[i];j++){ int u = rol[i][j][0]; u = su[u] - ty[u] + rol[i][j][1]; ma[i][u] = 1; } } flag = cnt ; for(int i = 0;i < n; i++){ for(int j = 0;j < ty[i]; j++){ int u = su[i] - ty[i] + j; ma[u][cnt+i] = 1; for(int k = 0;k < ty[i]; k++){ int vv = su[i] - ty[i] + k; ma[u][vv] = 1; } } } prepare(cnt ,cnt + n); for(int i = 0;i < cnt; i++){ for(int j = 0;j < cnt +n; j++){ if(ma[i][j]) Link(i+1,j+1); } } maxmum = n; int x = Dance(0); printf("Case %d: %d\n",tt,maxmum); } return 0; } /* 剪枝说明 1,选取一列未覆盖的列,选取在该列上为1的所有行,把这些行对应的所有列都覆盖 2,重复1直到所有列被覆盖得到f(),可知最少需要f()行才能覆盖所有列 输出结果:在Dance中如果返回1则记录该点 */