搜索模板笔记

搜索

DLX

精确覆盖

选择\(n\)行精确覆盖\(m\)列。

给一些小地图选取一些恰好拼成地图

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 1006
#define maxno 1000006
int L[maxno], R[maxno], U[maxno], D[maxno], Col[maxno], Row[maxno];
int H[maxn], S[maxn];
int ans, ansp[maxn], n, m, si;
void init(){
	for(int i=0; i<=m; i++){
		S[i]=0;
		U[i]=D[i]=i;
		L[i]=i-1;
		R[i]=i+1;
	}
	R[m]=0, L[0]=m;
	si=m;
	memset(H, -1, sizeof(H));
}
void link(int r, int c){
	si++, S[c]++;
	Col[si]=c, Row[si]=r;
	U[si]=U[c];
	D[U[c]]=si;
	D[si]=c;
	U[c]=si;
	if(H[r]==-1) H[r]=L[si]=R[si]=si;
	else{
		L[si]=L[H[r]];
		R[L[H[r]]]=si;
		R[si]=H[r];
		L[H[r]]=si;
	}
}

void remove(int c){
	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];
			S[Col[j]]--;
		}
	}
}

void resume(int c){
	for(int i=U[c]; i!=c; i=U[i]){
		for(int j=L[i]; j!=i; j=L[j]){
			U[D[j]]=j;
			D[U[j]]=j;
			S[Col[j]]++;
		}
	}
	L[R[c]]=c;
	R[L[c]]=c;
}

bool DLX(int d){
	if(R[0]==0){
		ans=d;
		return 1;
	}
	int c=R[0];
	for(int i=R[0]; i; i=R[i]) {
		if(S[i]<S[c]) c=i;//Sum最大的列
	}
	remove(c);
	for(int i=D[c]; i!=c; i=D[i]) {
		ansp[d]=Row[i];
		for(int j=R[i]; j!=i; j=R[j]) remove(Col[j]);
		if(DLX(d+1)) return 1;
		for(int j=L[i]; j!=i; j=L[j]) resume(Col[j]);
	}
	resume(c);
	return 0;
}

int main() {
	while(~scanf("%d%d",&n,&m)) {
		init();
		for(int i=1; i<=n; i++) {
			int t1, t2;
			scanf("%d",&t1);
			for(int j=0; j<t1; j++) {
				scanf("%d",&t2);
				link(i, t2);
			}
		}
		ans=-1;
		if(!DLX(0)) printf("No\n");
		else {
			printf("%d", ans);
			for(int i=0; i<ans; i++) {
				printf(" %d", ansp[i]);
			}
			printf("\n");
		}
	}
	return 0;
}
可重复覆盖

实质是找确定第一行后按遍历顺序找覆盖所有列后行的最小值,遍历顺序相同时以不同行开始有不同的最小值,可按要求选择最大最小值和最小最小值等。

给一些雷达选定k个覆盖每个城市,求雷达R,二分答案

#include <bits/stdc++.h>//DLX模板 
using namespace std;
#define ll long long
#define maxn 1006
#define maxno 1000006
int L[maxno], R[maxno], U[maxno], D[maxno], Col[maxno], Row[maxno];
int H[maxn], S[maxn];
int ans, ansp[maxn], n, m, si;
void init(){
	for(int i=0; i<=m; i++){
		S[i]=0;
		U[i]=D[i]=i;
		L[i]=i-1;
		R[i]=i+1;
	}
	R[m]=0, L[0]=m;
	si=m;
	memset(H, -1, sizeof(H));//改for
//	memset(S, 0, sizeof(-1));
}
void link(int r, int c){
	si++, S[c]++;
	Col[si]=c, Row[si]=r;
	U[si]=U[c];
	D[U[c]]=si;
	D[si]=c;
	U[c]=si;
	if(H[r]==-1) H[r]=L[si]=R[si]=si;
	else{
		L[si]=L[H[r]];
		R[L[H[r]]]=si;
		R[si]=H[r];
		L[H[r]]=si;
	}
}

void remove(int c){
	for(int i=D[c]; i!=c; i=D[i]){
		L[R[i]]=L[i];
		R[L[i]]=R[i];
	}
}

void resume(int c){
	for(int i=U[c]; i!=c; i=U[i]){
		L[R[i]]=i;
		R[L[i]]=i;
	}
}

int step2(){
	int ans=0;
	bool vis[maxn];
	memset(vis, 0, sizeof(vis));
	for(int c=R[0]; c; c=R[c]){
		if(!vis[c]){
			ans++;
			vis[c]=1;
			for(int i=D[c]; i!=c; i=D[i])
				for(int j=R[i]; j!=i; j=R[j])
					vis[Col[j]]=1;
		}
	}
	return ans;
}

void DLX(int d){
	if(ans!=-1 && d+step2()>=ans) return;
	if(R[0]==0){
		if(ans==-1) ans=d;
		else ans=min(ans, d);
		return;
	}
	int c=R[0];
	for(int i=R[0]; i; i=R[i]) {
		if(S[i]<S[c]) c=i;//Sum最大的列
	}
	for(int i=D[c]; i!=c; i=D[i]) {
		ansp[d]=Row[i];
		remove(i);
		for(int j=R[i]; j!=i; j=R[j]) remove(j);
		DLX(d+1);
		for(int j=L[i]; j!=i; j=L[j]) resume(j);
		resume(i);
	}
	return;
}

int main() {
	while(~scanf("%d%d",&n,&m)) {
		init();
		int t1, t2, t3;
		scanf("%d", &t1);
		for(int i=0; i<t1; i++) {
			scanf("%d %d", &t2, &t3);
			link(t2, t3);
		}
		ans=-1;
		DLX(0);
		printf("%d\n", ans);
//		for(int i=0; i<ans; i++) {
//			printf(" %d", ansp[i]);
//		}
//		printf("\n");
	}
	return 0;
}
//题目给出必须选择某些行
void remove2(int r){
	if(H[r]==-1) return;
	for(int i=D[H[r]]; i!=H[r]; i=D[i]){
		if(H[Row[i]]==i){
			if(R[i]==i) H[Row[i]]=-1;
			else H[Row[i]]=R[i];
		}
		L[R[i]]=L[i];
		R[L[i]]=R[i];
	}
	for(int i=R[H[r]]; i!=H[r]; i=R[i]){
		for(int j=D[i]; j!=i; j=D[j]){
			if(H[Row[j]]==j){
				if(R[j]==j) H[Row[j]]=-1;
				else H[Row[j]]=R[j];
			}
			L[R[j]]=L[j];
			R[L[j]]=R[j];
		}
	}
}
posted @ 2021-03-02 14:54  bqlp  阅读(63)  评论(0)    收藏  举报