POJ 2547 - No Tipping
http://poj.org/problem?id=2547
概要
長さ L,重さ W の板がある.この板の中心を座標 0 とし,左方向が負,右方向が正とする. 板は -1.5, +1.5 の位置にある2つの支点によって支えられている.
板の上には重りが N 個乗っており,それぞれ座標 x[i],重さ w[i] である.
板が倒れないように,1つずつ重りを取り除きたいので,その手順を答える. ただし,どう取り除いても途中で倒れてしまう場合は「Impossible」と答える.
板が倒れるのは,左側の支点を中心としたトルクが反時計回りの方向ときか,右側の支点を中心としたトルクが時計回りの方向のときである.
制約
- L >= 3
- N <= 20
解法
板の上にある重りの状態は高々 2^20 通りなので,メモりながら DFS する.
status を見ると 0MS 近くで通されているので,もっと高速な方法がありそう.
poj/2547.cc1 #include <cstdio> 2 using namespace std; 3 4 struct package { int x, w; }; 5 int N, W; 6 package ps[20]; 7 int ans[20]; 8 9 bool ok(unsigned s) 10 { 11 double left = 1.5 * W, right = -1.5 * W; 12 for (int i = 0; i < N; i++) { 13 if (s & (1<<i)) { 14 left += ps[i].w * (ps[i].x + 1.5); 15 right += ps[i].w * (ps[i].x - 1.5); 16 } 17 } 18 return left >= 0.0 && right <= 0.0; 19 } 20 21 bool valid[1<<20]; 22 int memo[1<<20]; 23 24 bool dfs(unsigned s, int n) 25 { 26 if (memo[s] != -1) { 27 return memo[s]; 28 } 29 if (s == 0) { 30 return true; 31 } else { 32 for (int i = 0; i < N; i++) { 33 if (ans[i] == -1) { 34 const unsigned t = s & ~(1<<i); 35 ans[i] = n; 36 if (valid[t] && dfs(t, n+1)) { 37 return (memo[s] = true); 38 } 39 ans[i] = -1; 40 } 41 } 42 return (memo[s] = false); 43 } 44 } 45 46 int main() 47 { 48 int L; 49 for (int Ti = 1; scanf("%d %d %d", &L, &W, &N) != EOF && L != 0; Ti++) { 50 printf("Case %d:\n", Ti); 51 for (int i = 0; i < N; i++) { 52 scanf("%d %d", &ps[i].x, &ps[i].w); 53 ans[i] = -1; 54 } 55 56 for (unsigned s = 0; s < (1<<N); s++) { 57 valid[s] = ok(s); 58 memo[s] = -1; 59 } 60 61 if (dfs((1<<N)-1, 0)) { 62 package qs[20]; 63 for (int i = 0; i < N; i++) { 64 qs[ans[i]] = ps[i]; 65 } 66 for (int i = 0; i < N; i++) { 67 printf("%d %d\n", qs[i].x, qs[i].w); 68 } 69 } else { 70 puts("Impossible"); 71 } 72 } 73 return 0; 74 }