Uva 307木棍、387谜题、10160服务站总结
2014-04-23 13:47
316 查看
本文所讨论的几个问题均为NP完全问题,所以自然解决的办法就是智能穷举,设计算法时为穷举选择合适的剪枝是在短时间内解决问题的最佳选择。
以上就是解决这个问题的源代码,uva:runtime 0.193s ranking 46
本人没有使用此法,就是利用简单的穷举加回溯得到了Uva AC。不过时间多了很多2.2s。
,想尽各种办法,如果不对地图进行拆分,基本上都是TLE。本来想要转载此文,并进行进一步说明,心想也没有太大必要,原文也很不错。
博客里有源码:Uva: runtime 0.029s ranking 70。
1.木棍问题
#include <cstring> #include <algorithm> #include <cstdio> using namespace std; #define maxn 65 int n, sum, goal; int stick[maxn]; bool visit[maxn]; bool cmp(const int &a, const int &b) { return a > b; } bool dfs(int now, int index, int cnt) { if(goal * cnt == sum) return true;//所有木棍拼凑成功 if(stick[n-1]>goal-now)return false;//当前所剩的最短木棍的长度都大于现阶段拼凑原木棍剩下的长度,无需继续递归 for(int i = index; i < n; i++) {//由于对木棍按长度排序了,所以i的初值是index而不是0,我们才选择小木棍时,无需考虑大木棍,排序确保了大木棍在之前处理了。 if(visit[i] || (i && !visit[i-1] && stick[i] == stick[i-1])) continue;//两个木棍长度一样,那么使用前一个无法成功,自然使用这个也无法成功。 if(now + stick[i] == goal) { visit[i] = true; if(dfs(0, 0, cnt + 1)) return true; visit[i] = false; //若使用此木棍最后拼成了当前原木棍,但是,后面的原木棍无法拼凑成功,则无需继续递归。 //举例说明:若此木块长为x,你可以从后面选取总长为x的木块若干,再次拼凑出该原木棍。但是,依然无法利用此木棍长度为x拼凑成功后面的原木棍。 return false; } else if(now + stick[i] < goal) { visit[i] = true; if(dfs(now + stick[i], i + 1, cnt)) return true; visit[i] = false; if(now == 0) return false;//对于拼凑当前“全新”的原木棍,若使用木棍无法拼凑成功,后面任一“全新”原木棍都不能含它,题意要求使用所有木棍,无需继续递归。 } } return false; } int solve() { sort(stick, stick + n, cmp); for(goal = stick[0]; goal < sum; goal++) { if(sum % goal != 0) continue; memset(visit, false, sizeof(visit)); if(dfs(0, 0, 0)) break; } return goal; } int main() { while(~scanf("%d", &n)) { if(!n) break; sum = 0; for(int i = 0; i < n; i++) { scanf("%d", &stick[i]); sum += stick[i]; } printf("%d\n", solve()); } return 0; }
以上就是解决这个问题的源代码,uva:runtime 0.193s ranking 46
2.387谜题
对于这个问题,请参考精确覆盖问题,利用Dancing Link优化。#include<cstdio> #include<cstring> #define MAXM 10 #define MAXN 100000 #define MAXL 110 #define INF 0x7FFFFFFF struct node { int h, l; char s[MAXM][MAXM]; }; struct answer { int pos, h, l; }; answer ans[MAXL]; node sq[MAXM]; bool vis[MAXL]; int L[MAXN], R[MAXN], U[MAXN], D[MAXN];//上下左右 int H[MAXN], S[MAXN], C[MAXN], X[MAXN]; int size; char a[MAXM][MAXM]; void Init(int m) { int i; memset(vis, false, sizeof(vis)); for (i = 0; i <= m; i++) { L[i + 1] = i; R[i] = i + 1; U[i] = D[i] = i; S[i] = 0; } R[m] = 0; size = m + 1; } void Link(int r, int c) { U[size] = c; D[size] = D[c]; U[D[c]] = size; D[c] = size; if (H[r] < 0) H[r] = L[size] = R[size] = size; else { L[size] = H[r]; R[size] = R[H[r]]; L[R[H[r]]] = size; R[H[r]] = size; } S[c]++; X[size] = r; C[size++] = c; } void Remove(int c) {//缩减双向十字链表 int i, j; L[R[c]] = L[c]; R[L[c]] = R[c]; for (i = D[c]; i != c; i = D[i]) { for (j = R[i]; j != i; j = R[j]) { U[D[j]] = U[j]; D[U[j]] = D[j]; S[C[j]]--; } } } void Resume(int c) {//恢复双向十字链表 int i, j; L[R[c]] = R[L[c]] = c; for (i = D[c]; i != c; i = D[i]) { for (j = R[i]; j != i; j = R[j]) { U[D[j]] = D[U[j]] = j; S[C[j]]++; } } } bool Dance() { if (R[0] == 0) return true; int i, j, temp, c; for (temp = INF,i = R[0]; i; i = R[i]) { if (temp > S[i]) { temp = S[i]; c = i; } } Remove(c); for (i = D[c]; i != c; i = D[i]) { vis[X[i]] = true; for (j = R[i]; j != i; j = R[j]) Remove(C[j]); if (Dance()) return true; for (j = L[i]; j != i; j = L[j]) Resume(C[j]); vis[X[i]] = false; } Resume(c); return false; } void Build(int n) { int i, j, k, t, p, r; for (i = r = 0; i < n; i++) { for (j = 0; j <= 4 - sq[i].h; j++) { for (k = 0; k <= 4 - sq[i].l; k++) { H[++r] = -1; Link(r, 16 + i + 1); ans[r].pos = i; ans[r].h = j; ans[r].l = k; for (t = 0; t < sq[i].h; t++) { for (p = 0; p < sq[i].l; p++) { if (sq[i].s[t][p] != '0') Link(r, 4 * (j + t) + k + p + 1); } } } } } } int main() { //freopen("C:\\Users\\Ron\\Desktop\\sample.txt","r",stdin); int n, i, j, k; bool flag = true; while (scanf("%d", &n), n) { if (flag) flag = false; else putchar('\n'); Init(16 + n); for (i = 0; i < n; i++) { scanf("%d%d", &sq[i].h, &sq[i].l); for (j = 0; j < sq[i].h; j++) { for (k = 0; k < sq[i].l; k++) scanf(" %c", &sq[i].s[j][k]); } } Build(n); if (Dance()) { for (i = 1; i < MAXL; i++) { if (vis[i]) { for (j = 0; j < sq[ans[i].pos].h; j++) { for (k = 0; k < sq[ans[i].pos].l; k++) { if (sq[ans[i].pos].s[j][k] != '0') a[j + ans[i].h][k + ans[i].l] = '1' + ans[i].pos; } } } } for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) putchar(a[i][j]); putchar('\n'); } } else puts("No solution possible"); } while(true); return 0; }此为387谜题问题,dancing link源代码,因为我自己看的也是云山雾绕也没加注释。 Uva:runtime 0.089s ranking 67。有兴趣的朋友可以深入研究精确覆盖问题。
本人没有使用此法,就是利用简单的穷举加回溯得到了Uva AC。不过时间多了很多2.2s。
3.服务站
请参考寂静山林博客,想尽各种办法,如果不对地图进行拆分,基本上都是TLE。本来想要转载此文,并进行进一步说明,心想也没有太大必要,原文也很不错。
博客里有源码:Uva: runtime 0.029s ranking 70。
相关文章推荐
- UVA-10160-servig stations(服务站)
- uva 10160服务站
- UVa Problem 10160 Servicing Stations (服务站)
- 习题7-14 小木棍 UVa307
- uva 387(回溯)
- UVa227 谜题 Puzzle
- 暴力,STL,哈希技术,Floyd判圈算法(计算器谜题,uva 11549)
- UVA-10003 切木棍,区间dp
- uva11549 - Calculator Conundrum(计算机谜题)
- uva 11205 The Broken Pedometer(经典的子集生成题目,在此总结了三种子集生成的方法~)
- 题解:谜题(UVa 227)
- UVA 10003 切木棍
- 关于UVa - 2017.4.3总结
- uva 387
- UVA 307 Sticks
- UVA 10003 Cutting Sticks 切木棍 dp
- 【UVa 10003】【区间DP】Cutting Sticks【有一个长为L的木棍,木棍中间有n个切点。每次切割的费用为当前木棍的长度。求切割木棍的最小费用。】
- uva 10160 Servicing Stations
- UVa做题阶段性总结(2014-11-11)
- UVA-307 Sticks (DFS+剪枝)