DLX专题总结

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

DLX专题总结其实我个⼈⾮常喜欢DLX.
因为我认为他较为简单——建模 + DLX = AC!
这⾥先分享⼀套我较为常⽤的模板:
const int N = 9;
const int maxn = N*N*N + 10;
const int maxnode=maxn*4+maxn+10;
const int INF=0x3f3f3f3f;
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列
int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
}
bool dance(int k){//精确覆盖
//(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最⼩
if(!R[0]){
//cnt=min(cnt,k);//精确匹配输出最⼩
for(int i = 0;i < k; ++i)
out[(ans[i]-1)/N]=(ans[i]-1)%N+1; //数独输出
cnt=k;
return1;
}
int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
if(dance(k+1))return1;
//dance(k+1);//精确匹配输出最⼩
FF(j,L,i)add(col[j]);
}
add(c);
return0;
}
void remove(int c){//重复覆盖
FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c){//重复覆盖
FF(i,U,c)L[R[i]]=R[L[i]]=i;
}
int A(){//估价函数
int res=0;
bool vis[m+1];
memset(vis,0,sizeof(vis));
FF(i,R,0)if(!vis[i]){
res++,vis[i]=1;
FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
}
return res;
}
void dance(int now,int &ans){//重复覆盖,需要传⼊⼀个接收答案的ans
if(R[0]==0)ans=min(ans,now);
else if (now+A()>=limit) return ;
else if(now+A()<ans){
int temp=INF,c;
FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
FF(i,D,c){
remove(i);
FF(j,R,i)remove(j);
dance(now+1,ans);
FF(j,L,i)resume(j);
resume(i);
}
}
}
}dlx;
View Code
具体的原理我就不再说了,毕竟⽹上已经有很多优秀的博客。

(其实就是我也不是很懂原理,就知道⼗字交叉链表这⾥主要是为了给我刷的kuangbin专题写个总结。

顺便写写我个⼈对DLX的理解吧。

(优化其实都在我给的板⼦⾥有所体现,这⾥就不说了)
DLX最难的地⽅就是建模了。

⾸先我们需要弄明⽩⾏跟列分别代表了什么:
⾏——所有的发⽣情况 列——所有限制条件
就拿来举例吧。

()
⾸先⼀个三阶数独,总共有81个格⼦。

每个格⼦都可以摆上1-9。

所以,所有的发⽣情况就是9 x 81 = 729 也就是⾏数了。

之后⼀个三阶数独有4个⼤的限制条件:
⼀、所有的格⼦上必须都有数字摆放(81个格⼦)
⼆、每⼀⾏上都要恰好有1-9这就个数字(9⾏ X 9个数字 = 81)
三、每⼀列上都要恰好有1-9这就个数字(9列 X 9个数字 = 81)
四、每⼀个宫⾥⾯都要恰好有1-9这就个数字(9宫 X 9个数字 = 81)
所以,所有的限制条件就有(81 + 81 + 81 +81 )种,也就是列数了
(这⾥放⼀张⽹上嫖来的图⽅便理解)
之后解题思路就是,根据题⽬给出的数据,遍历矩阵⾥的每⼀个数字。

分为两种情况:
⼀、如果是题⽬给出的数字1-9
(仅仅将这个情况LINK)
LINK(⾏, 格⼦限制), LINK(⾏, ⾏限制), LINK(⾏, 列限制), LINK(⾏, 宫限制);
⼆、如果是0
(枚举每个数字在这个格⼦⾥的情况,将其LINK)
for (int i = 1;i <= 9; ++i)
LINK(⾏, 格⼦限制), LINK(⾏, ⾏限制), LINK(⾏, 列限制), LINK(⾏, 宫限制);
之后DANCE就完事⼉了。

输出的话就是存的时候的⾏号处理⼀下输出就⾏。

这⾥给出这道题的代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 9;
const int maxn = N*N*N + 10;
const int maxnode=maxn*4+maxn+10;
const int INF=0x3f3f3f3f;
char g[maxn];
inline void read(int &sum) {
char ch = getchar();
int tf = 0;
sum= 0;
while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
tf=((ch == '-') && (ch=getchar()));
while(ch>='0' && ch<='9') sum=sum*10+(ch-48),ch=getchar();
(tf) && (sum =- sum);
}
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
// #define maxnode 50000
// #define maxn 750
// #define maxm 750
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
}
bool dance(int k){//精确覆盖
//(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最⼩
if(!R[0]){
//cnt=min(cnt,k);//精确匹配输出最⼩
for(int i = 0;i < k; ++i)
out[(ans[i]-1)/9]=(ans[i]-1)%9+1;
cnt=k;
output();
return1;
}
int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
if(dance(k+1))return1;
//dance(k+1);//精确匹配输出最⼩
FF(j,L,i)add(col[j]);
}
add(c);
return0;
}
// void remove(int c){//重复覆盖
// FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
// }
// void resume(int c){//重复覆盖
// FF(i,U,c)L[R[i]]=R[L[i]]=i;
// }
// int A(){//估价函数
// int res=0;
// bool vis[m+1];
// memset(vis,0,sizeof(vis));
// FF(i,R,0)if(!vis[i]){
// res++,vis[i]=1;
// FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
// }
// return res;
// }
// void dance(int now,int &ans){//重复覆盖,需要传⼊⼀个接收答案的ans
// if(R[0]==0)ans=min(ans,now);
// else if(now+A()<ans){
// int temp=INF,c;
// FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
// FF(i,D,c){
// remove(i);
// FF(j,R,i)remove(j);
// dance(now+1,ans);
// FF(j,L,i)resume(j);
// resume(i);
// }
// }
// }
void output() {
for (int i = 0; i < cnt; ++i)
cout << out[i];
cout << endl;
}
}dlx;
int main()
{
while(scanf("%s",g) == 1)
{
if(strcmp(g,"end") == 0) break;
dlx.init(N * N * N, N * N * 4);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for(int k = 1;k <= N;++k)
if(g[i * N + j] == '.' || g[i * N + j] == '0' + k)
{
dlx.link((i*N+j)*N+k, i*N+j+1);
dlx.link((i*N+j)*N+k, N*N+i*N+k);
dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
dlx.link((i*N+j)*N+k, N*N*3+((i/3)*3+(j/3))*N+k);
}
dlx.dance(0);
}
return0;
}
View Code
剩下的就都是题⽬的代码和建模思路了。

先是精确覆盖,这东西⾮常的快。

如果能转换成精确覆盖那么DLX必然是你的最佳选择。

这道题就是很经典的DLX精确覆盖裸题了。

建模思路:
⾏:所有的拼图数 P
列:所有的⾯积 N*M(题⽬要求所有的⾯积都要被覆盖)
给出代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
int n, m, p;
inline void read(int &sum) {
char ch = getchar();
int tf = 0;
sum= 0;
while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
tf=((ch == '-') && (ch=getchar()));
while(ch>='0' && ch<='9') sum=sum*10+(ch-48),ch=getchar();
(tf) && (sum =- sum);
}
struct DLX{
#define FF(i,A,s) for(int i = A[s];i != s;i = A[i])
#define maxnode 500010
#define maxn 550
#define maxm 1050
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列int sz,col[maxnode],row[maxnode],s[maxm],H[maxn];//H每⾏的头结点,s每列的节点数
int ans[maxm],cnt;
void init(int m){
for(int i=0;i<=m;++i)
L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
memset(H,-1,sizeof(H)),memset(s,0,sizeof(s));
L[0]=m,R[m]=0,sz=m+1;
cnt = INF;
}
void link(int r,int c)
{
++ s[col[++ sz] = c];
row[sz] = r;
D[sz] = D[c];
U[D[c]] = sz;
U[sz] = c;
D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else
{
R[sz] = R[H[r]];
L[R[H[r]]] = sz;
L[sz] = H[r];
R[H[r]] = sz;
}
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--s[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++s[col[U[D[j]]=D[U[j]]=j]];
}
bool dance(int k){//精确覆盖
//if (cnt != -1)
if(!R[0]){
cnt=min(k, cnt);return1;
}
int c=R[0];FF(i,R,0)if(s[c]>s[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
dance(k+1);
FF(j,L,i)add(col[j]);
}
add(c);
return0;
}
void remove(int c){//重复覆盖
FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c){//重复覆盖
FF(i,U,c)L[R[i]]=R[L[i]]=i;
}
int A(){//估价函数
int res=0;
bool vis[m+1];
memset(vis,0,sizeof(vis));
FF(i,R,0)if(!vis[i]){
res++,vis[i]=1;
FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
}
return res;
}
void dance(int now,int &ans){//重复覆盖,需要传⼊⼀个接收答案的ans
if(R[0]==0)ans=min(ans,now);
else if(now+A()<ans){
int temp=INF,c;
FF(i,R,0)if(temp>s[i])temp=s[i],c=i;
FF(i,D,c){
remove(i); FF(j,R,i)remove(j);
dance(now+1,ans);
FF(j,L,i)resume(j); resume(i);
}
}
}
void output() {
// for (int i = 0; i < cnt; ++i)
// cout <<ans[i]<< ((i==cnt-1)?"\n":" ");
cout << cnt << endl;
}
}dlx;
int main(){
int T; read(T);
while (T--) {
read(n), read(m), read(p);
dlx.init(n*m);
int x1,x2,y1,y2;
for (int i = 1; i <= p; ++i){
read(x1),read(y1),read(x2),read(y2);
for (int x = x1+1; x<= x2; ++x)
for (int y = y1+1; y <= y2; ++y)
dlx.link(i, y+(x-1)*m);
}
dlx.dance(0);
cout << ((t==INF)?-1:t)<< endl;
}
return0;
}
View Code
这道题就不讲了太裸了
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
int n, m;
inline bool read(int &sum) {
char ch = getchar();
int tf = 0;
sum = 0;
while((ch < '0' || ch > '9') && (ch != '-')) ch = getchar();
tf = ((ch == '-') && (ch = getchar()));
while(ch >= '0' && ch <= '9') sum = sum * 10+ (ch - 48), ch = getchar();
(tf) && (sum =- sum);
return1;
}
struct DLX{
#define FF(i,A,s) for(int i = A[s];i != s;i = A[i])
#define maxn 6305
int L[maxn],R[maxn],U[maxn],D[maxn];//每个点的左右上下指针,所在⾏列int sz,col[maxn],row[maxn],s[maxn],H[maxn];//H每⾏的头结点,s每列的节点数int ans[maxn],cnt;
void init(int m){
for(int i=0;i<=m;++i)
L[i]=i-1,R[i]=i+1,U[i]=D[i]=i;
memset(H,-1,sizeof(H)),memset(s,0,sizeof(s));
L[0]=m,R[m]=0,sz=m+1;
}
void link(int r,int c)
{
++ s[col[++ sz] = c];
row[sz] = r;
D[sz] = D[c];
U[D[c]] = sz;
U[sz] = c;
D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else
{
R[sz] = R[H[r]];
L[R[H[r]]] = sz;
L[sz] = H[r];
R[H[r]] = sz;
}
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--s[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++s[col[U[D[j]]=D[U[j]]=j]];
}
bool dance(int k){//精确覆盖
if(!R[0]){
cnt=k;return1;
}
int c=R[0];FF(i,R,0)if(s[c]>s[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i]; if(dance(k+1))return1;
FF(j,L,i)add(col[j]);
}
add(c);
return0;
}
void remove(int c){//重复覆盖
FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c){//重复覆盖
FF(i,U,c)L[R[i]]=R[L[i]]=i;
}
// int A(){//估价函数
// int res=0;
// bool vis[m+1];
// memset(vis,0,sizeof(vis));
// FF(i,R,0)if(!vis[i]){
// res++,vis[i]=1;
// FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
// }
// return res;
// }
// void dance(int now,int &ans){//重复覆盖,需要传⼊⼀个接收答案的ans
// if(R[0]==0)ans=min(ans,now);
// else if(now+A()<ans){
// int temp=INF,c;
// FF(i,R,0)if(temp>s[i])temp=s[i],c=i;
// FF(i,D,c){
// remove(i);
// FF(j,R,i)remove(j);
// dance(now+1,ans);
// FF(j,L,i)resume(j);
// resume(i);
// }
// }
// }
}dlx;
int main(){
cin.tie(0);
ios::sync_with_stdio(0);
while (cin >> n >> m) {
dlx.init(m);
int temp;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
read(temp);
if (temp) dlx.link(i,j);
}
if (dlx.dance(0)) cout << "Yes, I found it" << endl;
else cout << "It is impossible" << endl;
}
return0;
}
View Code
这道题就只是四阶数独⽽已,只是将N改为了16。

建模的思路与之前的3阶数独是⼀样的。

就不讲了(注意这⾥有些OJ的题⾯给的样例是错的,可以使⽤我注释⾥的)#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 16;
const int maxn = N*N*N + 10;
const int maxnode=maxn*4+maxn+10;
const int INF=0x3f3f3f3f;
string g;
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列
int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
}
bool dance(int k){//精确覆盖
//(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最⼩
if(!R[0]){
//cnt=min(cnt,k);//精确匹配输出最⼩
for(int i = 0;i < k; ++i)
out[(ans[i]-1)/16]=(ans[i]-1)%16+1;
cnt=k;
output();
return1;
}
int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
if(dance(k+1))return1;
//dance(k+1);//精确匹配输出最⼩
FF(j,L,i)add(col[j]);
}
add(c);
return0;
}
void output() {
for (int i = 0; i < cnt; ++i) {
cout << (char)(out[i]-1+'A');
if ((i+1)%16==0) cout << endl;
}
}
}dlx;
int main() {
string temp; int cnt = -1;
while (cin >> temp) {
g.clear(), g += temp;
for (int i = 1; i < 16; ++i) cin >> temp, g += temp;
dlx.init(N * N * N, N * N * 4);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for(int k = 1; k <= N;++k) {
if(g[i * N+j] == '-' || g[i * N + j] == 'A'+k-1) {
dlx.link((i*N+j)*N+k, i*N+j+1);
dlx.link((i*N+j)*N+k, N*N+i*N+k);
dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
dlx.link((i*N+j)*N+k, N*N*3+((i/4)*4+(j/4))*N+k);
}
}
if (++cnt) cout << endl;
dlx.dance(0);
}
return0;
}
/*
--A----C-----O-I
-J--A-B-P-CGF-H-
--D--F-I-E----P-
-G-EL-H----M-J--
----E----C--G---
-I--K-GA-B---E-J
D-GP--J-F----A--
-E---C-B--DP--O-
E--F-M--D--L-K-A
-C--------O-I-L-
H-P-C--F-A--B---
---G-OD---J----H
K---J----H-A-P-L
--B--P--E--K--A-
-H--B--K--FI-C--
--F---C--D--H-N-
*/
View Code
这道题倒是挺有趣的,难的地⽅在他的宫的构建(虽然也不难
我的想法是先BFS预处理⼀遍每个的宫,并将其标号,并将宫⾥的格⼦⽤宫号标记
之后就是常规数独的做法——LINK(⾏, 格⼦限制), LINK(⾏, ⾏限制), LINK(⾏, 列限制), LINK(⾏, 格⼦对应的宫号限制);之后就看我的代码吧。

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 9;
const int maxn = N*N*N + 10;
const int maxnode=maxn*4+maxn+10;
const int INF=0x3f3f3f3f;
int g[N][N];
int wall[N][N][4];//⽤于标记墙
int id[N][N]; //⽤于划分宫
int dic[4][2]={0,-1, 1,0, 0,1, -1,0};
int gong_id;
int res[N*N]; //⽤于最后输出
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列
int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m, cnt=0;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
}
int dance(int k){//精确覆盖
//(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最⼩
if(!R[0]){
//cnt=min(cnt,k);//精确匹配输出最⼩
for(int i = 0; i < k; ++i)
out[(ans[i]-1)/N]=(ans[i]-1)%N+1;
++cnt, memcpy(res, out, sizeof(res));
if (cnt > 1) return -1;
}
int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
if (dance(k+1)==-1) return -1;
//dance(k+1);//精确匹配输出最⼩
FF(j,L,i)add(col[j]);
}
add(c);
if (cnt > 1) return -1;
return0;
}
}dlx;
void BFS(int x, int y) {
queue<pair<int, int> > q;
q.push({x,y}), id[x][y] = ++gong_id;
while (!q.empty()) {
pair<int, int> top = q.front(); q.pop();
for (int i = 0; i < 4; ++i) {
int tx = top.first + dic[i][0];
int ty = top.second + dic[i][1];
if (wall[top.first][top.second][i] || id[tx][ty]) continue;
id[tx][ty] = gong_id, q.push({tx,ty});
}
}
}
int main() {
int T; cin >> T;
for (int z = 1; z <= T; ++z) {
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j) {
cin >> g[i][j],
wall[i][j][0] = wall[i][j][1] = wall[i][j][2] = wall[i][j][3] = id[i][j] = 0;
if (g[i][j] >= 128) g[i][j] -= 128, wall[i][j][0]=1; //left
if (g[i][j] >= 64) g[i][j] -= 64, wall[i][j][1]=1; //down
if (g[i][j] >= 32) g[i][j] -= 32, wall[i][j][2]=1; //right
if (g[i][j] >= 16) g[i][j] -= 16, wall[i][j][3]=1; //up
}
gong_id = 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
if (!id[i][j]) BFS(i,j);
dlx.init(N * N * N, N * N * 4);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for(int k = 1; k <= N;++k) {
if(g[i][j] == 0 || g[i][j] == k) {
dlx.link((i*N+j)*N+k, i*N+j+1);
dlx.link((i*N+j)*N+k, N*N+i*N+k);
dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
dlx.link((i*N+j)*N+k, N*N*3+(id[i][j]-1)*N+k);
}
}
cout << "Case "<< z << ":" << endl;
if (dlx.dance(0) == -1) cout << "Multiple Solutions" << endl;
else if (t)
for (int i = 0; i < N*N; ++i) {
cout << res[i];
if ((i+1)%N==0) cout << endl;
}
else cout << "No solution" << endl;
}
return0;
}
View Code
之后是重复覆盖,这东西⾮常⽞学。

我有⼏次都会超时,有⼏次⼜不超。

重复覆盖剪枝⾮常的重要,如果不剪枝就相当于是暴⼒回溯,这样的话他的解空间就会⾮常的⼤。

必然会超时。

但是说到剪枝,我所遇到的也就只是A()优化⽽已。

建模思路:
⾏:(N-K+1)*(M-K+1) 所有可以被喷格⼦数
列:所有的敌⼈数(因为所有的敌⼈都要被覆盖)
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 9;
const int maxn = N*N*N + 10;
const int maxnode=maxn*4+maxn+10;
const int INF=0x3f3f3f3f;
int g[N][N];
int wall[N][N][4];//⽤于标记墙
int id[N][N]; //⽤于划分宫
int dic[4][2]={0,-1, 1,0, 0,1, -1,0};
int gong_id;
int res[N*N]; //⽤于最后输出
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m, cnt=0;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void del(int c){//精确覆盖,删除涉及C列的集合
L[R[c]]=L[c],R[L[c]]=R[c];
FF(i,D,c)FF(j,R,i)U[D[j]]=U[j],D[U[j]]=D[j],--S[col[j]];
}
void add(int c){ //精确覆盖,恢复涉及C列的集合
R[L[c]]=L[R[c]]=c;
FF(i,U,c)FF(j,L,i)++S[col[U[D[j]]=D[U[j]]=j]];
}
int dance(int k){//精确覆盖
//(剪枝)if(cnt != -1 && cnt <= d) return 0;//精确匹配输出最⼩
if(!R[0]){
//cnt=min(cnt,k);//精确匹配输出最⼩
for(int i = 0; i < k; ++i)
out[(ans[i]-1)/N]=(ans[i]-1)%N+1;
++cnt, memcpy(res, out, sizeof(res));
if (cnt > 1) return -1;
}
int c=R[0];FF(i,R,0)if(S[c]>S[i])c=i;
del(c);
FF(i,D,c){
FF(j,R,i)del(col[j]);
ans[k]=row[i];
if (dance(k+1)==-1) return -1;
//dance(k+1);//精确匹配输出最⼩
FF(j,L,i)add(col[j]);
}
add(c);
if (cnt > 1) return -1;
return0;
}
}dlx;
void BFS(int x, int y) {
queue<pair<int, int> > q;
q.push({x,y}), id[x][y] = ++gong_id;
while (!q.empty()) {
pair<int, int> top = q.front(); q.pop();
for (int i = 0; i < 4; ++i) {
int tx = top.first + dic[i][0];
int ty = top.second + dic[i][1];
if (wall[top.first][top.second][i] || id[tx][ty]) continue;
id[tx][ty] = gong_id, q.push({tx,ty});
}
}
}
int main() {
int T; cin >> T;
for (int z = 1; z <= T; ++z) {
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j) {
cin >> g[i][j],
wall[i][j][0] = wall[i][j][1] = wall[i][j][2] = wall[i][j][3] = id[i][j] = 0;
if (g[i][j] >= 128) g[i][j] -= 128, wall[i][j][0]=1; //left
if (g[i][j] >= 64) g[i][j] -= 64, wall[i][j][1]=1; //down
if (g[i][j] >= 32) g[i][j] -= 32, wall[i][j][2]=1; //right
if (g[i][j] >= 16) g[i][j] -= 16, wall[i][j][3]=1; //up
}
gong_id = 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
if (!id[i][j]) BFS(i,j);
dlx.init(N * N * N, N * N * 4);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
for(int k = 1; k <= N;++k) {
if(g[i][j] == 0 || g[i][j] == k) {
dlx.link((i*N+j)*N+k, i*N+j+1);
dlx.link((i*N+j)*N+k, N*N+i*N+k);
dlx.link((i*N+j)*N+k, N*N*2+j*N+k);
dlx.link((i*N+j)*N+k, N*N*3+(id[i][j]-1)*N+k);
}
}
cout << "Case "<< z << ":" << endl;
if (dlx.dance(0) == -1) cout << "Multiple Solutions" << endl;
else if (t)
for (int i = 0; i < N*N; ++i) {
cout << res[i];
if ((i+1)%N==0) cout << endl;
}
else cout << "No solution" << endl;
}
return0;
}
View Code
这道题⽬⽼恶⼼了。

给每个规模下的⽅块标号。

⾸先预处理每个规模下每根⽕柴对应的⽅块位置。

(这块代码我是嫖⽹上的)
建模思路:
⾏:每根⽕柴(2*N*(N+1))
列:所有的⽅块(N*N + (N-1)*(N-1) + ... 1*1)
但是题⽬有可能会先拿⾛⼏根⽕柴。

这部分就不是很好处理。

⽹上有些是利⽤DLX内部的删除函数来删除的,但是我个⼈是喜欢⽤hash来写的。

(其实就是不懂DLX的原理不会删)
先将给出的⽕柴标记,并将⽕柴所对应的⽅块标记,之后把剩余的⽅块hash。

(那么列数就是后⾯的hash数)
后⾯通过hash来Link即可。

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iomanip>
using namespace std;
typedef long long ll;
const int N = 60 + 5;
const int maxn = 1*1+2*2+3*3+4*4+5*5+5;
const int maxnode=maxn*N+10;
const int INF=0x3f3f3f3f;
int n, m;
int match[6][N][maxn];//矩阵⼤⼩,⽕柴标号,对应⼩框⼦, //第三级的0是⽤来记录影响了⼏个格⼦的
bool isUsed[maxn];
int hash_col[maxn];
void init()
{
//总⽅格边长
for(int len=1;len<=5;len++) {
int cnt=0;
//⼩⽅格⼤⼩
for(int sz=1;sz<=len;sz++)
//起点横坐标
for(int i=1;i<=len+1-sz;i++)
//起点纵坐标
for(int j=0;j<len+1-sz;j++) {
++cnt;
for(int k=0;k<sz;k++) match[len][i+j*(2*len+1)+k][++match[len][i+j*(2*len+1)+k][0]]=cnt;
for(int k=0;k<sz;k++) match[len][i+(j+sz)*(2*len+1)+k][++match[len][i+(j+sz)*(2*len+1)+k][0]]=cnt;
for(int k=0;k<sz;k++) match[len][i+len+(j+k)*(2*len+1)][++match[len][i+len+(j+k)*(2*len+1)][0]]=cnt;
for(int k=0;k<sz;k++) match[len][i+len+sz+(j+k)*(2*len+1)][++match[len][i+len+sz+(j+k)*(2*len+1)][0]]=cnt; }
}
}
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列
int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void remove(int c){//重复覆盖
FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c){//重复覆盖
FF(i,U,c)L[R[i]]=R[L[i]]=i;
}
int A(){//估价函数
int res=0;
bool vis[m+1];
memset(vis,0,sizeof(vis));
FF(i,R,0)if(!vis[i]){
res++,vis[i]=1;
FF(j,D,i)FF(k,R,j)vis[col[k]]=1;
}
return res;
}
void dance(int now,int &ans){//重复覆盖,需要传⼊⼀个接收答案的ans
if(R[0]==0) {
ans=min(ans,now);
return ;
}
else if(now+A()<ans){
int temp=INF,c;
FF(i,R,0)if(temp>S[i])temp=S[i],c=i;
FF(i,D,c){
remove(i);
FF(j,R,i)remove(j);
dance(now+1,ans);
FF(j,L,i)resume(j);
resume(i);
}
}
}
}dlx;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T;
init();
while (T--) {
cin >> n >> m;
memset(isUsed, 0, sizeof(isUsed));
memset(hash_col, 0, sizeof(hash_col));
for (int i = 0; i < m; ++i) {
int temp; cin >> temp;
for (int j=1; j <= match[n][temp][0]; ++j)
isUsed[match[n][temp][j]]=1;
}
int limit = 0; for (int i = 1; i <= n; ++i) limit += i*i;
int col = 0;
for (int i = 1; i <= limit; ++i) if (!isUsed[i]) hash_col[i]=++col;
dlx.init(2*n*(n+1), col);
for (int i = 1; i <= 2*n*(n+1); ++i)
for (int j = 1; j <= match[n][i][0]; ++j)
if (hash_col[match[n][i][j]])
dlx.link(i, hash_col[match[n][i][j]]);
int ans = INF;
dlx.dance(0,ans);
cout << ans << endl;
}
return0;
}
View Code
这道题⾮常的经典。

⼆分答案+DLXcheck
建模思路:
⾏:雷达数量 M
列:城市数量 N (每个城市都需要被覆盖)
但是这道题还给了⼀个K操作员的条件,所以就可以放在A()⾥剪枝#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iomanip>
using namespace std;
typedef long long ll;
const int N = 50+5;
const int maxn = N*N + 10;
const int maxnode=maxn*N+10;
const int INF=0x3f3f3f3f;
bool g[N][N];
int n, m, k;
struct node {
int x, y;
bool operator < (const node& a) const {
if (x == a.x) return y < a.y;
return x < a.x;
}
}r[N],c[N];
struct node1 {
double val;
int id;
bool operator <(const node1& a) const{
return val < a.val;
}
};
vector<node1> dist[N];
struct DLX{
#define FF(i,A,S) for(int i = A[S];i != S;i = A[i])
int L[maxnode],R[maxnode],U[maxnode],D[maxnode];//每个点的左右上下指针,所在⾏列
int sz,col[maxnode],row[maxnode],S[maxn],H[maxn];//H每⾏的头结点,S每列的节点数
int ans[maxn],cnt;
int out[maxn];
int n,m;
void init(int _n,int _m) {
n=_n, m=_m;
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] = sz = m;
for(int i = 1;i <= n; ++i) H[i] = -1;
}
void link(int r,int c) {
++S[col[++sz]=c], row[sz] = r;
D[sz] = D[c], U[D[c]] = sz;
U[sz] = c, D[c] = sz;
if(H[r] < 0) H[r] = L[sz] = R[sz] = sz;
else R[sz]=R[H[r]], L[R[H[r]]]=sz,L[sz]= H[r],R[H[r]] = sz;
}
void remove(int c){//重复覆盖
FF(i,D,c)L[R[i]]=L[i],R[L[i]]=R[i];
}。

相关文档
最新文档