UVA 10123 - No Tipping

题目大意:在一块长L重量M的木板上放n个木块,下n行表示木块放的位置和木块的重量,开始木板是处于平衡的,每次拿下一个木块,直到木块全部被取下,期间要求木板不发生偏移,如果可以完全取下,就输出去下木块的顺序(情况不唯一),不能完全取下就输出Impossible。


解题思路:

<1>可以将木块取下的问题看成是将木块放上木板。

<2>要分清楚什么时候木板会倾斜,因为它有两个支点,所以要考虑两种情况,(wl1:-L ~ 1.5 ,wr1:1.5 ~ L 和  wl2:-L ~ -1.5 , wr2:-1.5 ~ L),要注意的是wl1 和 wr2 下面还有一个支点,所以  wl1 > wr1  和   wr2 > wl2 的时候木板是不会发生偏移的。(这里提一下,F = L * W ,   对于质量均匀的木盘呢,L取长度的中间值)  

<3>接下来要讲点关键的优化了,在计算完原先木板的力矩之后呢, 可以将处于(-1.5~1.5)这个区域的木块放到木板上,不难发现,这个地方的木块只会加大木板的稳定度(对应实际问题就是最后拿下来)。

<4>第二个优化就是该解决掉那部分会导致木块发生移动的木块了,首先将木块按位置分成放左边和放右边的两份,为什么要分开来考虑呢,因为同堆的木块对木板的偏移趋势是相同的,这对有助于后面用到的贪心的方法,只需将所有木块遍历一遍就可以的到结果。

<5>将两堆木块中的木块分别按照力矩的大小从小到大排列,然后从任意一堆中每次取小的放入木板,直到下一个木块放入后会发生移动,然后就换放另一堆的木块,同样的终止条件,直到木块全部放上去为止,这中间有个终止的特殊条件,就是不能全部放上去的情况,对应的就是两堆木块均无法放入的情况。


#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

int cnt[3], cur[3];
double  wl1, wr1, wl2, wr2;

struct Block {
	int pos;
	int w;
} block[3][30], record[30], x;

int cmp(const void *c, const void *d) {
	Block *a = (Block *)c;
	Block *b = (Block *)d;
	return abs(a->pos * a->w) > abs(b->pos * b->w);
}

int DFS(int dir, bool real) {
	int ok = false;
	for (int i = cur[dir]; i < cnt[dir]; i++) {
		if (dir == 0) {
			if (wr1 < wl1 +  (-3 - block[dir][i].pos) * block[dir][i].w)
				break;
			wl1 += (-3 - block[dir][i].pos) * block[dir][i].w;
			wl2 += ( 3 - block[dir][i].pos) * block[dir][i].w;
		}
		else {
			if (wl2 < wr2 + (block[dir][i].pos - 3) * block[dir][i].w) 
				break;
			wr2 += (block[dir][i].pos - 3) * block[dir][i].w;
			wr1 += (block[dir][i].pos + 3) * block[dir][i].w;
		}

		ok = true;
		record[cnt[1]] = block[dir][i];
		cur[dir]++;
		cnt[1]++;
	}

	if (ok || real)
		DFS (2 - dir, false);
}

int main() {
	int l, m, n, t = 0; 
	while (scanf("%d%d%d", &l, &m, &n), l) {
		//初始化
		memset(block, 0, sizeof(block));
		memset(cnt, 0, sizeof(cnt));
		memset(cur, 0, sizeof(cur));
		wl1 = wr2 = (l - 3) * (l - 3) * m / (l * 4.0);
		wr1 = wl2 = (l + 3)* (l + 3)  * m / (l * 4.0);
		//读入数据
		for (int i = 0; i < n; i++) {
			scanf("%d%d", &x.pos, &x.w);
			x.pos *= 2;
			if (x.pos > 3)
				block[2][cnt[2]++] = x;
			else if (x.pos < -3)
				block[0][cnt[0]++] = x;
			else
				block[1][cnt[1]++] = x;
		}
		//处理数据
		for (int i = 0; i < 3; i++)
			qsort(block[i], cnt[i], sizeof(block[0][0]), cmp);
		memcpy(record, block[1], sizeof(block[1]));
		for (int i = 0; i < cnt[1]; i++) {
			wr1 += (block[1][i].pos + 3) * block[1][i].w;
			wl2 += (3 - block[1][i].pos) * block[1][i].w;
		}
		DFS(0, true);
		//输出结果
		printf("Case %d:\n", ++t);
		if (cnt[1] == n)
			for (int i = n - 1; i >= 0; i--)
				printf("%d %d\n", record[i].pos / 2, record[i].w);
		else
			printf("Impossible\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰阔落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值