DLX 舞蹈链 精确覆盖+可重复覆盖
2016-08-13 10:28
246 查看
做了几道DLX 感觉这种板子很像网络流的使用
就是建矩阵
DLX大致有两种 精确覆盖+可重复覆盖
选择行使得所有的列里面都存在1的变量,前者是唯一的,后者是不唯一的,很简单
所以两个板子(窝也只能用板子,,,,,套啊套,在做题中理解板子
两个板子能达到的功能是 记录选择的行,限制行选择的个数,判断是否能覆盖(这当然是最基础的
两个不同的地方也就是remove 和 resume 两个函数
限制行数的函数在第二个板子里面有
先是精确覆盖
下面是几个板子题
精确覆盖:
poj 3074 板子一套就没有
hust 1071 还是板子题,用来练手,把选择到的行数输出就ok了,很简单,板子里面都有,在dance里面的ans数组存着
hdu 3663 从这个题就可以看出来DLX的使用和网络流很相似就是建模型
(最后选择的行是乱序的哦,,,
这个题我们可以将所有的可能被取区间当作的行,然后列为每个站的所有1-d的所有天数,为了每个站的值取一个区间我们,在列里面添加一行,这一行都置为1,是的同个站的区间只取一次,其实就是很简单的建模,有木有?
代码贴上来
可重复覆盖
hdu 5046:
DLX的板子可以实现限制行数的选择去找,那么就很简单了,二分+套板子
二分我们可以将距离离散化一下,这样可以快点
代码入下
就是建矩阵
DLX大致有两种 精确覆盖+可重复覆盖
选择行使得所有的列里面都存在1的变量,前者是唯一的,后者是不唯一的,很简单
所以两个板子(窝也只能用板子,,,,,套啊套,在做题中理解板子
两个板子能达到的功能是 记录选择的行,限制行选择的个数,判断是否能覆盖(这当然是最基础的
两个不同的地方也就是remove 和 resume 两个函数
限制行数的函数在第二个板子里面有
先是精确覆盖
struct DLX { int n,m,size; int U[maxn],D[maxn],R[maxn],L[maxn],Row[maxn],Col[maxn]; int H[maxn],S[maxn];//n,m的范围 int ansd; 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] = m; size = m; size = m; for(int i=1;i<=n;i++)H[i] = -1; } void Link(int r,int c){ ++S[Col[++size]=c]; Row[size] = r; D[size] = D[c]; U[D[c]] = size; U[size] = c; D[c] = size; if(H[r] < 0)H[r] = L[size] = R[size] = size; else{ R[size] = R[H[r]]; L[R[H[r]]] = size; L[size] = H[r]; R[H[r]] = size; } } //这里的remove其实并没有真正删除掉结点,可以用resume恢复 void remove(int c) //删除第c列上的元素所在行 { L[R[c]] = L[c]; R[L[c]] = R[c]; //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了 for(int i = D[c];i != c;i = D[i]) //c所在列上的元素i for(int j = R[i];j != i;j = R[j]) //i和j一行的,删掉j { U[D[j]] = U[j]; D[U[j]] = D[j]; --S[Col[j]]; //j所在列的元素(‘1’的个数)-1; } } void resume(int c) //对应的恢复操作 { for(int i = U[c];i != c;i = U[i]) for(int j = L[i];j != i;j = L[j]) ++S[Col[U[D[j]]=D[U[j]]=j]]; L[R[c]] = R[L[c]] = c; } void Dance(int d) { //剪枝下 if(ansd != -1 && ansd <= d)return; if(R[0] == 0) { if(ansd == -1)ansd = d; else if(d < ansd)ansd = d; return; } int c = R[0]; for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i; //找元素最少的列c,一种优化 remove(c); //删除列c for(int i = D[c];i != c;i = D[i]) { for(int j = R[i];j != i;j = R[j])remove(Col[j]); //删除所有可能的冲突元素 Dance(d+1); for(int j = L[i];j != i;j = L[j])resume(Col[j]); } resume(c); } }; /*ans.init() ans.Link(x,y) ans.ansd = -1 ans.Dance(0) tmp.ansd > 0 ? */ int n,m; DLX tmp; while(cin >> n >> m){ tmp.init(n,m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ int a;cin >> a; if(a)tmp.Link(i,j);//存入1 } } tmp.ansd = -1;//答案 tmp.Dance(0); if(tmp.ansd > 0);//满足情况 else ;//不能满足情况 }可重复覆盖
//限制行数,可重复覆盖 struct DLX { int L[maxnode],R[maxnode],U[maxnode],D[maxnode]; int Row[maxnode],Col[maxnode]; int S[maxn],H[maxn]; int ansd; int n,m,size; 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] = m; size = m; for(int i=1;i<=n;i++)H[i] = -1; } void Link(int r,int c){ ++S[Col[++size]=c]; Row[size] = r; D[size] = D[c]; U[D[c]] = size; U[size] = c; D[c] = size; if(H[r] < 0)H[r] = L[size] = R[size] = size; else{ R[size] = R[H[r]]; L[R[H[r]]] = size; L[size] = H[r]; R[H[r]] = size; } } 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]] = R[L[i]] = i; } } bool v[1000];//限制选择的行数 int f()//计算选择的行数 { int ret = 0; for(int c=R[0];c!=0;c=R[c])v[c] = 1; for(int c=R[0];c!=0;c=R[c]){ if(v[c]){ ret++; v[c] = 0; for(int i=D[c];i!=c;i=D[i]){ for(int j=R[i];j!=i;j=R[j]){ v[Col[j]]=0; } } } } return ret; } bool Dance(int d){ if(d+f()>k)return 0;//选择不能大于k行 if(R[0] == 0)return d <= k; int c = R[0]; for(int i=R[0];i!=0;i=R[i]) if(S[i] < S[c])c = i; for(int i=D[c];i!=c;i=D[i]){ remove(i); for(int j=R[i];j!=i;j=R[j])remove(j); if(Dance(d+1))return 1; for(int j=L[i];j!=i;j=L[j])resume(j); resume(i); } return 0; } }; DLX ans;ans.init(n,n);ans.ansd = -1; ans.Link(i,j); ans.Dance(0);
下面是几个板子题
精确覆盖:
poj 3074 板子一套就没有
hust 1071 还是板子题,用来练手,把选择到的行数输出就ok了,很简单,板子里面都有,在dance里面的ans数组存着
hdu 3663 从这个题就可以看出来DLX的使用和网络流很相似就是建模型
(最后选择的行是乱序的哦,,,
这个题我们可以将所有的可能被取区间当作的行,然后列为每个站的所有1-d的所有天数,为了每个站的值取一个区间我们,在列里面添加一行,这一行都置为1,是的同个站的区间只取一次,其实就是很简单的建模,有木有?
代码贴上来
/* ^^ ====== ^^ ID: meixiuxiu PROG: test LANG: C++11 */ #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <climits> #include <string> #include <vector> #include <cmath> #include <stack> #include <queue> #include <set> #include <map> #include <sstream> #include <cctype> #include <bitset> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int ,int> pii; #define MEM(a,b) memset(a,b,sizeof a) #define CLR(a) memset(a,0,sizeof a); #define pi acos(-1.0) #define maxnode 360005 #define maxn 1005 const int inf = 0x3f3f3f3f; const int MOD = 1e9 + 7; std::vector<int> edge[100]; int ans[maxnode]; int U[maxnode],D[maxnode],R[maxnode],L[maxnode]; int to[10005]; int Row[maxnode],Col[maxnode];//元素对应的行列 struct DLX { int H[maxn],S[maxn];//s记录该列残余的1个数,h该行最左端的1 int ansd; int n,m,size; 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] = m; size = m; for(int i=1;i<=n;i++)H[i] = -1; } void Link(int r,int c){ ++S[Col[++size]=c]; Row[size] = r; D[size] = D[c]; U[D[c]] = size; U[size] = c; D[c] = size; if(H[r] < 0)H[r] = L[size] = R[size] = size; else{ R[size] = R[H[r]]; L[R[H[r]]] = size; L[size] = H[r]; R[H[r]] = size; } } //这里的remove其实并没有真正删除掉结点,可以用resume恢复 void remove(int c) //删除第c列上的元素所在行 { L[R[c]] = L[c]; R[L[c]] = R[c]; //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了 for(int i = D[c];i != c;i = D[i]) //c所在列上的元素i for(int j = R[i];j != i;j = R[j]) //i和j一行的,删掉j { U[D[j]] = U[j]; D[U[j]] = D[j]; --S[Col[j]]; //j所在列的元素(‘1’的个数)-1; } } void resume(int c) //对应的恢复操作 { for(int i = U[c];i != c;i = U[i]) for(int j = L[i];j != i;j = L[j]) ++S[Col[U[D[j]]=D[U[j]]=j]]; L[R[c]] = R[L[c]] = c; } int Dance(int d) { //剪枝下 if(ansd != -1 && ansd <= d)return 1; if(R[0] == 0) { if(ansd == -1)ansd = d; else if(d < ansd)ansd = d; return 1; } int c = R[0]; for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i; //找元素最少的列c,一种优化 remove(c); //删除列c for(int i = D[c];i != c;i = D[i]) { ans[d] = Row[i];//记录最后选择的行 for(int j = R[i];j != i;j = R[j])remove(Col[j]); //删除所有可能的冲突元素 if(Dance(d+1))return 1; for(int j = L[i];j != i;j = L[j])resume(Col[j]); } resume(c); return 0; } }; int vis[66][66]; int main() { #ifdef LOCAL freopen("in.txt", "r", stdin); // freopen("out.txt","w",stdout); #endif int n,m,d; while(cin >> n >> m >> d){ DLX tmp; MEM(vis,0); for(int i=1;i<=n;i++)edge[i].clear(); for(int i=1;i<=m;i++){ int u,v;scanf("%d%d",&u,&v); if(vis[u][v])continue; vis[u][v] = vis[v][u] = 1; edge[u].push_back(v); edge[v].push_back(u); } int _n = 0, _m = 0; int s[100],e[100]; int mp[1000][2]; for(int i=1;i<=n;i++){ scanf("%d%d",&s[i],&e[i]); edge[i].push_back(i); _n += e[i]-s[i]+1 + 1 + (e[i]-s[i]+1)*(e[i]-s[i])/2; } _m = n*d+n; tmp.init(_n,_m); int row = 0; int xx = 0; for(int i=1;i<=n;i++){ tmp.Link(++row,n*d+i); to[row] = i; mp[row][0] = mp[row][1] = 0; for(int j=s[i];j<=e[i];j++){ for(int k=j;k<=e[i];k++){ ++row; mp[row][0] = j; mp[row][1] = k; to[row] = i; for(int t=0;t<edge[i].size();t++){ int v = edge[i][t]; for(int p = j;p <= k;p++){ tmp.Link(row,(v-1)*d+p); } } tmp.Link(row,n*d+i); xx = max(xx,n*d+i); } } } tmp.ansd = -1; if(tmp.Dance(0)){ int res[105][2]; for(int i=0;i<tmp.ansd;i++){ //printf("%d %d\n",mp[ans[i]][0],mp[ans[i]][1]); //cout << to[ans[i]] << endl; res[to[ans[i]]][0] = mp[ans[i]][0]; res[to[ans[i]]][1] = mp[ans[i]][1]; } for(int i=1;i<=n;i++){ printf("%d %d\n",res[i][0],res[i][1]); } } else { printf("No solution\n"); } puts(""); } return 0; }
可重复覆盖
hdu 5046:
DLX的板子可以实现限制行数的选择去找,那么就很简单了,二分+套板子
二分我们可以将距离离散化一下,这样可以快点
代码入下
/* ^^ ====== ^^ ID: meixiuxiu PROG: test LANG: C++11 */ #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <climits> #include <string> #include <vector> #include <cmath> #include <stack> #include <queue> #include <set> #include <map> #include <sstream> #include <cctype> #include <bitset> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int ,int> pii; #define MEM(a,b) memset(a,b,sizeof a) #define CLR(a) memset(a,0,sizeof a); #define pi acos(-1.0) #define maxnode 100*100 #define maxn 105 const int inf = 0x3f3f3f3f; const int MOD = 1e9 + 7; int k; struct DLX { int L[maxnode],R[maxnode],U[maxnode],D[maxnode]; int Row[maxnode],Col[maxnode]; int S[maxn],H[maxn]; int ansd; int n,m,size; 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] = m; size = m; for(int i=1;i<=n;i++)H[i] = -1; } void Link(int r,int c){ ++S[Col[++size]=c]; Row[size] = r; D[size] = D[c]; U[D[c]] = size; U[size] = c; D[c] = size; if(H[r] < 0)H[r] = L[size] = R[size] = size; else{ R[size] = R[H[r]]; L[R[H[r]]] = size; L[size] = H[r]; R[H[r]] = size; } } 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]] = R[L[i]] = i; } } bool v[1000];//限制选择的行数 int f()//计算选择的行数 { int ret = 0; for(int c=R[0];c!=0;c=R[c])v[c] = 1; for(int c=R[0];c!=0;c=R[c]){ if(v[c]){ ret++; v[c] = 0; for(int i=D[c];i!=c;i=D[i]){ for(int j=R[i];j!=i;j=R[j]){ v[Col[j]]=0; } } } } return ret; } bool Dance(int d){ if(d+f()>k)return 0;//选择不能大于k行 if(R[0] == 0)return d <= k; int c = R[0]; for(int i=R[0];i!=0;i=R[i]) if(S[i] < S[c])c = i; for(int i=D[c];i!=c;i=D[i]){ remove(i); for(int j=R[i];j!=i;j=R[j])remove(j); if(Dance(d+1))return 1; for(int j=L[i];j!=i;j=L[j])resume(j); resume(i); } return 0; } }; int x[105],y[105],n; bool check(ll limit){ DLX ans;ans.init(n,n);ans.ansd = -1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(1ll*fabs(x[i]-x[j])+1ll*fabs(y[i]-y[j])<=limit){ ans.Link(i,j); } } } if(ans.Dance(0))return 1; return 0; } int main() { #ifdef LOCAL freopen("in.txt", "r", stdin); // freopen("out.txt","w",stdout); #endif int t;cin >> t; int kase = 1; while(t--){ cin >> n >> k; for(int i=1;i<=n;i++)cin >> x[i] >> y[i]; ll tmp[10005];int c = 0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ tmp[++c] = 1ll*fabs(x[i]-x[j])+1ll*fabs(y[i]-y[j]); } } sort(tmp+1,tmp+1+c); int cnt = unique(tmp+1,tmp+1+c)-(tmp+1); int l = 1,r = cnt; while(l < r){ int mid = (l+r)>>1; //cout << tmp[mid] << endl; if(check(tmp[mid]))r = mid; else l = mid+1; } printf("Case #%d: %lld\n",kase++,tmp[l]); } return 0; }
相关文章推荐
- DLX 舞蹈链 精确覆盖 与 重复覆盖
- hdu 3957 Street Fighter 重复覆盖+精确覆盖 DLX
- (模板)dlx 精确覆盖和重复覆盖
- 【转】DLX 精确覆盖 重复覆盖
- HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)
- 最新版dlx模板(精确覆盖+重复覆盖)
- DLX 精确覆盖 重复覆盖
- DLX精确覆盖与重复覆盖模板题
- DLX模板之精确覆盖和重复覆盖
- DLX (精确区间覆盖,重复区间覆盖)(模板)
- SPOJ 1771&&DLX精确覆盖,重复覆盖
- HDU 3957 Street Fighter (最小支配集 DLX 重复覆盖+精确覆盖 )
- dancing link 精确覆盖 重复覆盖 (DLX)
- dancing link 精确覆盖 重复覆盖 (DLX)
- poj 3074 Sudoku (精确覆盖,DLX,搜索)
- DLX精确覆盖poj1086
- [DLX精确覆盖] hdu 1603 A Puzzling Problem
- HDU 2295 Radar(重复覆盖,DLX)
- DLX算法求解精确覆盖问题
- HUST 1017 - Exact cover——舞蹈链,精确点覆盖