題目:在一個有兩個支點的槓桿上(支點在-1.5,+1.5),已知桿長和質量,以及上面的重物,
問是否存在一個順序可以把所有的物品都拿下來,並且不打破平衡。
分析:狀態壓縮、記憶化搜索。數據較小,利用位表示選取狀態,每次兩個支點分別判斷平衡即可。
說明:注意開始時的平衡狀態,dp也可以╮(╯▽╰)╭。
#include <cstdio>
#include <cstring>
int states[1<<20], front[1<<20], space[20], weight[20];
double balance(int n, int select, double support)
{
double left = 0, right = 0;
for (int i = 0; i < n; ++ i)
if ((1<<i)&select) {
if (space[i] < support) {
left += weight[i]*(support-space[i]);
}else {
right += weight[i]*(space[i]-support);
}
}
return left - right;
}
int test(int n, int select, double w)
{
return (balance(n, select, -1.5)-w <= 0)&&(balance(n, select, 1.5)+w >= 0);
}
int save[20];
int dfs(int n, int d, int state, double w)
{
if (!states[state]) {
states[state] = 1;
}else {
return 0;
}
if (!state) {
for (int i = 0; i < n; ++ i)
printf("%d %d\n",space[save[i]], weight[save[i]]);
return 1;
}
for (int i = 0; i < n; ++ i) {
if ((state&(1<<i)) && test(n, state-(1<<i), w)) {
save[d] = i;
if (dfs(n, d+1, state-(1<<i), w))
return 1;
}
}
return 0;
}
int main()
{
int l, m, n, cases = 1;
while (~scanf("%d%d%d",&l,&m,&n) && l+m+n) {
for (int i = 0; i < n; ++ i)
scanf("%d%d",&space[i],&weight[i]);
printf("Case %d:\n",cases ++);
memset(states, 0, sizeof(states));
if (test(n, (1<<n)-1, 0.75*m) && !dfs(n, 0, (1<<n)-1, 0.75*m))
puts("Impossible");
}
return 0;
}