hihoCoder #1317 : 搜索四·跳舞鏈
原題地址:http://hihocoder.com/problemset/problem/1317
時間限制:10000ms
單點時限:1000ms
內存限制:256MB
描述
小Ho最近遇到一個難題,他需要破解一個棋局。
棋局分成了n行,m列,每行有若干個棋子。小Ho需要從中選擇若干行使得每一列有且恰好只有一個棋子。
比如下面這樣局面:
其中1表示放置有棋子的格子,0表示沒有放置棋子。
對於上面這個問題,小Ho經過多次嘗試以后得到了解為選擇2、3、4行就可以做到。
但是小Ho覺得自己的方法不是太好,於是他求助於小Hi。
小Hi:小Ho你是怎么做的呢?
小Ho:我想每一行都只有兩種狀態,選中和未被選中。那么我將選中視為1,未選中視為0。則每一種組合恰好對應了一個4位的01串,也就是一個4位的二進制數。
小Hi:恩,沒錯。
小Ho:然后我所做的就是去枚舉每一個二進制數然后再來判定是否滿足條件。
小Hi:小Ho你這個做法本身沒什么問題,但是對於棋盤行數再多一點的情況就不行了。
小Ho:恩,我也這么覺得,那你有什么好方法么?
小Hi:我當然有了,你聽我慢慢道來。
輸入
第1行:1個正整數t,表示數據組數,1≤t≤10。
接下來t組數據,每組的格式為:
第1行:2個正整數n,m,表示輸入數據的行數和列數。2≤n,m≤100。
第2..n+1行:每行m個數,只會出現0或1。
輸出
第1..t行:第i行表示第i組數據是否存在解,若存在輸出"Yes",否則輸出"No"。
樣例輸入
2
4 4
1 1 0 1
0 1 1 0
1 0 0 0
0 1 0 1
4 4
1 0 1 0
0 1 0 0
1 0 0 0
0 0 1 1
樣例輸出
No
Yes
DLX精確覆蓋
#include <algorithm> #include <cstring> #include <string.h> #include <iostream> #include <list> #include <map> #include <set> #include <stack> #include <string> #include <utility> #include <queue> #include <vector> #include <cstdio> #include <cmath> #define LL long long using namespace std; const int maxnode = 100010; //最多多少個‘1’ const int MaxM = 1010; const int MaxN = 1010; struct DLX { int n,m,SIZE; int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];//L,R,D,U四個數組記錄某節點上下左右鄰居 int H[MaxN], S[MaxM];//H記錄排頭,S記錄某列有多少個節點 int ansd, ans[MaxN]; 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 exact_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 repeat_remove(int c) { for(int i = D[c]; i != c; i = D[i]) L[R[i]] = L[i], R[L[i]] = R[i]; } void repeat_resume(int c) { for(int i = U[c]; i != c; i = U[i]) L[R[i]] = R[L[i]] = i; } int f() { //估價函數。 bool vv[MaxM]; int ret = 0, c, i, j; for(c = R[0]; c != 0; c = R[c]) vv[c] = 1; for(c = R[0]; c != 0; c = R[c]) if(vv[c]) { ++ret, vv[c] = 0; for(i = D[c]; i != c; i = D[i]) for(j = R[i]; j != i; j = R[j]) vv[Col[j]] = 0; } return ret; } void repeat_dance(int d) { if(d + f() >= ansd) return; //估價函數剪枝,A*搜索 if(R[0] == 0) { if(d < ansd) ansd = d; return; } int c = R[0], i, j; for(i = R[0]; i; i = R[i]) if(S[i] < S[c]) c = i; for(i = D[c]; i != c; i = D[i]) { repeat_remove(i); for(j = R[i]; j != i; j = R[j]) repeat_remove(j); repeat_dance(d + 1); for(j = L[i]; j != i; j = L[j]) repeat_resume(j); repeat_resume(i); } } void exact_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; } //d為遞歸深度 bool exact_Dance(int d) { if(R[0] == 0) { ansd = d; return true; } int c = R[0]; for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i; exact_Remove(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]) exact_Remove(Col[j]); if(exact_Dance(d+1))return true; for(int j = L[i]; j != i;j = L[j]) exact_resume(Col[j]); } exact_resume(c); return false; } }; DLX g; int main() { int n,m; int t; cin>>t; while( t--) { scanf("%d%d",&n,&m); g.init(n,m); for(int i = 1;i <= n;i++) { int num; for(int j=1;j<=m;j++) { scanf("%d",&num); if(num==1) g.Link(i,j); } } if(!g.exact_Dance(0))printf("No\n"); else printf("Yes\n"); } return 0; }
HDU 3498 whosyourdaddy
http://acm.hdu.edu.cn/showproblem.php?pid=3498
whosyourdaddyTime Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem Description sevenzero liked Warcraft very much, but he haven't practiced it for several years after being addicted to algorithms. Now, though he is playing with computer, he nearly losed and only his hero Pit Lord left. sevenzero is angry, he decided to cheat to turn defeat into victory. So he entered "whosyourdaddy", that let Pit Lord kill any hostile unit he damages immediately. As all Warcrafters know, Pit Lord masters a skill called Cleaving Attack and he can damage neighbour units of the unit he attacks. Pit Lord can choice a position to attack to avoid killing partial neighbour units sevenzero don't want to kill. Because sevenzero wants to win as soon as possible, he needs to know the minimum attack times to eliminate all the enemys.
Input There are several cases. For each case, first line contains two integer N (2 ≤ N ≤ 55) and M (0 ≤ M ≤ N*N),and N is the number of hostile units. Hostile units are numbered from 1 to N. For the subsequent M lines, each line contains two integers A and B, that means A and B are neighbor. Each unit has no more than 4 neighbor units. The input is terminated by EOF.
Output One line shows the minimum attack times for each case.
Sample Input 5 4 1 2 1 3 2 4 4 5 6 4 1 2 1 3 1 4 4 5
Sample Output 2 3
Author sevenzero
Source 2010 ACM-ICPC Multi-University Training Contest(7)——Host by HIT |
DLX重復覆蓋
n個敵對單元,m對單元互相相鄰,建圖的鄰接矩陣,DLX。
#include <algorithm> #include <cstring> #include <string.h> #include <iostream> #include <list> #include <map> #include <set> #include <stack> #include <string> #include <utility> #include <queue> #include <vector> #include <cstdio> #include <cmath> #define LL long long using namespace std; const int maxnode = 100010; //最多多少個1 const int MaxM = 1010; const int MaxN = 1010; struct DLX { int n,m,SIZE; int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];//L,R,D,U四個數組記錄某節點上下左右鄰居 int H[MaxN], S[MaxM];//H記錄行頭,S記錄某列有多少個節點 int ansd, ans[MaxN]; 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 exact_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 repeat_remove(int c) { for(int i = D[c]; i != c; i = D[i]) L[R[i]] = L[i], R[L[i]] = R[i]; } void repeat_resume(int c) { for(int i = U[c]; i != c; i = U[i]) L[R[i]] = R[L[i]] = i; } int f() { //估價函數。 bool vv[MaxM]; int ret = 0, c, i, j; for(c = R[0]; c != 0; c = R[c]) vv[c] = 1; for(c = R[0]; c != 0; c = R[c]) if(vv[c]) { ++ret, vv[c] = 0; for(i = D[c]; i != c; i = D[i]) for(j = R[i]; j != i; j = R[j]) vv[Col[j]] = 0; } return ret; } void repeat_dance(int d) { if(d + f() >= ansd) return; //估價函數剪枝,A*搜索 if(R[0] == 0) { if(d < ansd) ansd = d; return; } int c = R[0], i, j; for(i = R[0]; i; i = R[i]) if(S[i] < S[c]) c = i; for(i = D[c]; i != c; i = D[i]) { repeat_remove(i); for(j = R[i]; j != i; j = R[j]) repeat_remove(j); repeat_dance(d + 1); for(j = L[i]; j != i; j = L[j]) repeat_resume(j); repeat_resume(i); } } void exact_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; } //d為遞歸深度 bool exact_Dance(int d) { if(R[0] == 0) { ansd = d; return true; } int c = R[0]; for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i; exact_Remove(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]) exact_Remove(Col[j]); if(exact_Dance(d+1))return true; for(int j = L[i]; j != i;j = L[j]) exact_resume(Col[j]); } exact_resume(c); return false; } }; DLX dlx; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { dlx.init(n,n); while(m--) { int a,b; scanf("%d%d",&a,&b); dlx.Link(a,b); dlx.Link(b,a); } for(int i=1;i<=n;i++)dlx.Link(i,i); dlx.ansd=0x3f3f3f; dlx.repeat_dance(0); printf("%d\n",dlx.ansd); } return 0; }