HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)

很久以前就看到的一个经典题,一直没做,今天拿来练手。街霸

给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人。

问最少选多少个角色(每个角色只能选一次),使得可以KO掉其他所有人(包括所有版本)。

典型的DLX。前∑mode[i]列表示被KO的人版本,重复覆盖。后n列表示选了的人,精确覆盖。

即,在精确覆盖满足的前提下,完成重复覆盖,且使所选行最少。

据说这题可以转化成只用一种覆盖,或者是dfs+剪枝。这里就先这样吧。

加了好多注释,方便以后看。

注意的是,dance的时候,要先删除重复覆盖,再删除精确覆盖。。。

2515MS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
using namespace std;

#define MP make_pair
#define ll long long
#define inf 0x3f3f3f3f

#define maxr 88
#define maxn (maxr*maxr)
struct DLX{
	int m;// amount of column
	int m1,m2;// amount of repeat column and exact column
	int L[maxn],R[maxn],U[maxn],D[maxn],cnt;
	int row[maxn],col[maxn];
	int N[maxr],use[maxr],head[maxr];
	void init(int _m){// may need modify this function
		m = _m;
		memset(head,-1,sizeof(head));
		memset(N,0,sizeof(N));
		for(int i=0;i<=m;++i){
			L[i]=i-1,R[i]=i+1;
			U[i]=D[i]=i;
			row[i]=0,col[i]=i;
		}
		L[0]=m,R[m]=0;
		cnt=m;
		best = inf;
	}
	void exrm(int c){// remove of exact cover, private
		L[R[c]]=L[c],R[L[c]]=R[c];
		for(int i=D[c];i!=c;i=D[i])
			for(int j=R[i];j!=i;j=R[j])
				U[D[j]]=U[j],D[U[j]]=D[j],--N[col[j]];
	}
	void exres(int c){// resume of exact cover, private
		for(int i=U[c];i!=c;i=U[i])
			for(int j=L[i];j!=i;j=L[j])
				U[D[j]]=D[U[j]]=j,++N[col[j]];
		L[R[c]]=R[L[c]]=c;
	}
	void rm(int x){// remove of repeat cover, private
		for(int i=D[x];i!=x;i=D[i])
			L[R[i]]=L[i],R[L[i]]=R[i];
	}
	void res(int x){// resume of repeat cover, private
		for(int i=D[x];i!=x;i=D[i])
			L[R[i]]=R[L[i]]=i;
	}
	int low(){// private, sometimes need modify this function
		int mi=maxr,idx=0;
		for(int i=R[0];i<=m1;i=R[i])if(N[i]<mi&&N[i])mi=N[i],idx=i;
		return idx;
	}
	void link(int r,int c){
		++N[c],++cnt;
		row[cnt]=r,col[cnt]=c;
		U[cnt]=U[c],D[cnt]=c;
		U[D[cnt]]=D[U[cnt]]=cnt;
		if(head[r]==-1)
			head[r]=L[cnt]=R[cnt]=cnt;
		else {
			L[cnt]=L[head[r]];
			R[cnt]=head[r];
			L[R[cnt]]=R[L[cnt]]=cnt;
		}
	}
	bool del[maxr];
	int cost2(){// lower_bound
		int ret=0;
		memset(del,false,sizeof(del));
		for(int c=R[0];c && c<=m1;c=R[c]){
			if(!del[c]){
				del[c]=true;
				ret++;
				for(int i=D[c];i!=c;i=D[i])
					for(int j=R[i];j!=i;j=R[j])
						del[col[j]]=true;
			}
		}
		return ret;
	}
	int best;
	void dance(int dep,int val){// always need modify this function
		if(R[0]==0 || R[0]>m1){
			best = min(best, val);
			return ;
		}
		int c=low();
		if(c==0)return ;
		if(dep+cost2()>=best) return ;
		for(int i=D[c];i!=c;i=D[i]){
			int r=row[i];
			use[dep]=i;
			rm(i);
			for(int j=R[i];j!=i;j=R[j]) if(col[j]<=m1) rm(j);
			for(int j=R[i];j!=i;j=R[j]) if(col[j]>m1) exrm(col[j]);
			dance(dep+1,val+1);
			for(int j=L[i];j!=i;j=L[j]) if(col[j]>m1) exres(col[j]);
			for(int j=L[i];j!=i;j=L[j]) if(col[j]<=m1) res(j);
			res(i);
		}
	}
}dlx;

int mode[30];
int sum[30];
vector<pair<int,int> >beat[30][2];
int main(){
	int t,ca=0;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;++i){
			scanf("%d",mode+i);
			if(i==0) sum[i] = mode[i];
			else sum[i] = sum[i-1]+mode[i];
			for(int j=0;j<mode[i];++j){
				int k,beatp,beatm;
				scanf("%d",&k);
				beat[i][j].clear();
				for(int kk=0;kk<k;++kk){
					scanf("%d%d",&beatp,&beatm);
					beat[i][j].push_back(MP(beatp,beatm));
				}
			}
		}
		dlx.init(sum[n-1]+n);
		dlx.m1 = sum[n-1], dlx.m2 = n;
		for(int i=0;i<n;++i){
			for(int j=0;j<mode[i];++j){
				int row = (i?sum[i-1]:0)+j+1;
				dlx.link(row,sum[n-1]+i+1);// exact cover
				dlx.link(row,(i?sum[i-1]:0)+1);// repeat cover
				if(mode[i]==2) dlx.link(row,(i?sum[i-1]:0)+2);// repeat cover
				for(int k=0;k<beat[i][j].size();++k){// repeat cover
					pair<int,int>tmp = beat[i][j][k];
					int beatp = tmp.first;
					int beatm = tmp.second;
					int col = (beatp?sum[beatp-1]:0)+beatm+1;
					dlx.link(row,col);
				}
			}
		}
		dlx.dance(0,0);
		printf("Case %d: %d
",++ca,dlx.best);
	}
    return 0;
}
原文地址:https://www.cnblogs.com/nextbin/p/3997796.html