题目:uva :10123 - No Tipping
题目大意:给出l, m, n 分别表示 长度为l 的杠杆, 重量为 m, 有n个物体放在上方。问每次从上面挑选一个物品移除,是否能使杠杆继续平衡。这个过程中都可以的话,就输出移除顺序(不唯一) 否则就输出 impossible ,一开始,这个杠杆就不平衡的情况也会是有的。因为杠杆也是有重量的。
解题思路;
1、这题先前我就不明白什么怎么样的情况下,双支撑点的杠杆不平横,后面看了别人的报告才明白。
首先 我这里有两个支撑点 (1, 2) 左边的为1. 然后1支撑点的左力距为wl1.同理还有wr1, wl2, wr2. 有1个支撑点的力距的值可以比没有支撑点的力距大,因为多个支撑点支撑重量。所以 这里不平横情况 ( wl1 > wr1 || wr2 > wl2).
2、这题还有时间的问题,直接去dfs是会超时的。所以这里就需要优化。
首先放在两个支撑点中间的物体会使得这个杠杆更加的平衡,因此,这样的物体可以最后移除。然后将力距分左右从小到大的排序。因为要使得移除的任何一个过程都需要平衡,所以问题可以转换为把一个一个物体放到一开始为空的杠杆上的状态和顺序,最后逆向输出。物体一个一个放的话,当然是力距小比较不容易发生失衡的现象。然后左边的物体从例力距小的开始放,如果不能放就换放右边的力距小的物体。如果两边都不能放就说明是impossible。
注意:这题中间dfs需要考虑仔细点,例如如何判断两边都不能放的情况,如果一边都放完的情况,还有这边放一些去另外一边的情况。
还有两支撑点间的物体虽然dfs不需要考虑,但是他们的力距需要加到总的力距上。他们的加入会使得杠杆更加平衡,能支撑更多的物体。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> #include <stdlib.h> #include <math.h> using namespace std; const int N = 30; int l, m, n, ans[N][2], path[3], cnt[3]; int flag; double wl1, wl2, wr1, wr2; struct OB { int w, l; } obl[N], obr[N], obm[N]; bool cmp (const OB & x,const OB &y) { if (abs (x.l * x.w) < abs (y.l * y.w)) return true; return false; } void solve (int dir, int cur, int bo) { if (cur >= n) { flag = 1; return; } if (dir == 0) { for (int i = path[dir]; i < cnt[dir]; i++) { // printf ("%.3lf %.3lf\n", wl1 + ( -3 - obl[i].l) * obl[i].w, wr1); if (wl1 + (-3 - obl[i].l) * obl[i].w > wr1) { if (bo) { flag = -1; return; } path[dir] = i; solve( 2 - dir, cur, bo + 1); if (flag) return; } else { ans[cur][0] = obl[i].l; ans[cur][1] = obl[i].w; wl1 += (-3 - obl[i].l) * obl[i].w; wl2 += (3 - obl[i].l) * obl[i].w; path[dir] = i + 1; cur++; bo = 0; } } if (path[2 - dir] <= cnt[2 - dir]) solve (2 - dir, cur, bo); } else { for (int i = path[dir]; i < cnt[dir]; i++) { if (wr2 + (obr[i].l - 3) * obr[i].w > wl2) { if (bo) { flag = -1; return; } path[dir] = i; solve (2 - dir, cur, bo + 1); if (flag) return; } else { ans[cur][0] = obr[i].l; ans[cur][1] = obr[i].w; wr1 += (obr[i].l + 3) * obr[i].w; wr2 += (obr[i].l - 3) * obr[i].w; path[dir] = i + 1; cur++; bo = 0; } } if (path[2 - dir] <= cnt[2 - dir]) solve (2 - dir, cur, bo); } if (flag) return; } int main () { int p, w, t = 0; while (scanf ("%d%d%d", &l, &m, &n), l || m || n) { wl1 = wr2 = (l - 3.0) * (l - 3.0) * m / l / 4.0; wl2 = wr1 = (l + 3.0) * (l + 3.0) * m / l / 4.0; // printf ("wl1 = %.3lfwr1 = %.3lf\n", wl1, wr1); memset (cnt, 0, sizeof (cnt)); for (int i = 0; i < n; i++) { scanf ("%d%d", &p, &w); p = p * 2; if (p <= 3 && p >= -3){ obm[cnt[1]].w = w; obm[cnt[1]].l = p; cnt[1]++; } else { if (p > 3) { obr[cnt[2]].w = w; obr[cnt[2]].l = p; cnt[2]++; } else { obl[cnt[0]].w = w; obl[cnt[0]].l = p; cnt[0]++; } } } sort (obl, obl + cnt[0], cmp); sort (obr, obr + cnt[2], cmp); memset (path, 0, sizeof (path)); for (int i = 0; i < cnt[1]; i++) { wr1 += (obm[i].l + 3) * obm[i].w; wl2 += (3 - obm[i].l) * obm[i].w; } printf ("Case %d:\n", ++t); flag = 0; if (wl1 <= wr1 && wr2 <= wl2) { solve (0, cnt[1], 0); } if (flag != 1) printf ("Impossible\n"); else { for (int i = n - 1; i >= cnt[1]; i--) printf ("%d %d\n", ans[i][0]/2, ans[i][1]); if (cnt[1] - 1 >= 0) for (int i = cnt[1] - 1; i >= 0; i--) printf ("%d %d\n", obm[i].l/2, obm[i].w); } } return 0; }