搜索模板笔记
搜索
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];
}
}
}

浙公网安备 33010602011771号