POJ 1084 Square Destroyer
Posted in PKU OJ on 四月 11th, 2010 by 纳米 – Be the first to comment2010-04-09,阅读题目,分析题目
2010-04-11,代码实现,发现理解错题意;重新写,一次AC,282ms;下午加一可行性剪枝,16ms
解题报告:
此问题可以转化为一个图论的问题,把火柴看作另A类结点,正方形看作B类结点。如果某根火柴在某个正方形的边上,那么我们就往这相应的两个结点添加一条边。这样,问题转化为求该图中,最少用多少A类结点可以覆盖所有的B类结点。当n=5时,A类结点数为:2n(n+1)=60;B类结点数为:n(n+1)(2n+1)/6=55。
可以见得,B类结点可以直接通过一个64位整型类储存。利用位运算,可以大大加快搜索速度。做法具体如下:
(1) 若有边(i, j)(分别属于A、B类结点,下同),则Aij = 0;否则Aij = 1;
(2) 对于每个确定的i,将Aij二进制编码至Bi中;
(3) 除去数据中没有的Bi,并编码初始状态st;
(4) 对于Bi按照可覆盖点数排序(优化1);
(5) 若i<j,且Bi和Bj效果是一致的,则删掉Bj(优化2);
(6) 计算opt[i] = Bi & B(i+1) & B(i+2) … & Bn(可行性剪枝)。
搜索过程,按照一般的方法做即可,添加最优性剪枝:若当前值大于阶段最剪枝,则跳出;添加可行性剪枝,若取剩余所有的Bi也无法达到目标,则剪枝。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | #include <stdio.h> #include <stdlib.h> const int maxn = 65, INF[] = {0, 1, 3, 6, 9, 12}; int es, n, m, answer; long long edge[maxn], opt[maxn], st; bool ev[maxn]; int input() { int k, t; scanf("%d%d", &es, &k); n = (es * es + es) << 1; for (int i = 1; i <= n; ++i) ev[i] = 1; for (int i = 1; i <= k; ++i) { scanf("%d", &t); ev[t] = 0; } return 0; } int build_graph() { long long nt = 1; m = es*(es+1)*(2*es+1)/6; for (int i = 1; i <= n; ++i) edge[i] = (1 << m) - 1; for (int l = 1; l <= es; ++l) for (int x0 = 1, x1 = l; x1 <= es; ++x0, ++x1) for (int y0 = 1, y1 = l; y1 <= es; ++y0, ++y1) { int w0 = (es+es+1)*(x0-1) + y0; int w1 = (es+es+1)*x1 + y0; for (int i = 1; i <= l; ++i) { edge[w0] -= nt; edge[w1] -= nt; ++w0, ++w1; } w0 = es*x0 + (es+1)*(x0-1) + y0; w1 = w0 + l; for (int i = 1; i <= l; ++i) { edge[w0] -= nt; edge[w1] -= nt; w0 += es+es+1; w1 += es+es+1; } nt <<= 1; } int j = 0; st = (1 << m) - 1; for (int i = 1; i <= n; ++i) if (ev[i]) { edge[++j] = edge[i]; } else { st &= edge[i]; } n = j; return 0; } int count(long long x) { x = (x & 0x5555555555555555ll) + (x>> 1 & 0x5555555555555555ll); x = (x & 0x3333333333333333ll) + (x>> 2 & 0x3333333333333333ll); x = (x & 0x0F0F0F0F0F0F0F0Fll) + (x>> 4 & 0x0F0F0F0F0F0F0F0Fll); x = (x & 0x00FF00FF00FF00FFll) + (x>> 8 & 0x00FF00FF00FF00FFll); x = (x & 0x0000FFFF0000FFFFll) + (x>>16 & 0x0000FFFF0000FFFFll); x = (x & 0x00000000FFFFFFFFll) + (x>>32 & 0x00000000FFFFFFFFll); return x; } int dfs(int k, long long st, int sum) { if (!st) { answer = sum-1 < answer ? sum-1 : sum-1; } else if (k <= n && sum < answer && !(st & opt[k])) { if ((st & edge[k]) != st) dfs(k+1, st & edge[k], sum + 1); dfs(k+1, st, sum); } return 0; } int comp(long long *x, long long *y) { return count(*x) - count(*y); } int work() { answer = INF[es]; qsort(&edge[1], n, sizeof(long long), (int(*) (const void*, const void*)) comp); for (int i = 1; i < n; ++i) { int k = i; for (int j = i + 1; j <= n; ++j) if ((st & edge[i]) != (st & edge[j])) edge[++k] = edge[j]; n = k; } opt[n] = edge[n]; for (int i = n - 1; i > 0; --i) opt[i] = opt[i+1] & edge[i]; dfs(1, st, 1); return 0; } int output() { printf("%d\n", answer); return 0; } int main() { int testCases; scanf("%d", &testCases); while (testCases--) { input(); build_graph(); work(); output(); } return 0; } |