uva 10123 - No Tipping

 

略蛋疼的一道题目,研究了好久,最开始简单的枚举回溯法,果断超时。网上看到了好多人的优化,最后选择了一种线性的解法,只需扫描一遍即可。

基本思路:将问题转化为依次往板上放包裹,为了让板尽可能的平衡,一定是先放扭矩小的上去,所以先将包裹分类,左支点左边的一波,中间的一波,右支点右边的一波,分别排序,把中间的一波先放上去,这样可以增加系统的稳定性,然后左右两边依次往上放直到一边失去平衡,换另一边上,如果两边都不能放还有剩余的话,就是没有解了。

 

思路参考:

http://www.ngszone.com/blog/archives/250

 

0.019s AC:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>

int len;
int wei;
int n;

typedef struct Pack {
	int x;
	int w;
	int torque;
} Pack;

Pack left[25];
Pack right[25];
Pack mid[25];
Pack all[25];
int lenL = 0, lenR = 0, lenM = 0, lenA = 0;

int isBalanced(int idx) {
	double weiLeftA = 0;
	double weiRightA = 0;
	double weiLeftB = 0;
	double weiRightB = 0;

	weiLeftA += 0.5 * (0.5 * len - 1.5) * (0.5 * len - 1.5) * wei / len;
	weiRightA += 0.5 * (0.5 * len + 1.5) * (0.5 * len + 1.5) * wei / len;
	weiLeftB += 0.5 * (0.5 * len + 1.5) * (0.5 * len + 1.5) * wei / len;
	weiRightB += 0.5 * (0.5 * len - 1.5) * (0.5 * len - 1.5) * wei / len;
	int i;
	for (i = 0; i < idx; i++) {
		if (all[i].x < -1.5)
			weiLeftA += (-1.5 - all[i].x) * all[i].w;
		if (all[i].x > -1.5)
			weiRightA += (all[i].x + 1.5) * all[i].w;
		if (all[i].x < 1.5)
			weiLeftB += (1.5 - all[i].x) * all[i].w;
		if (all[i].x > 1.5)
			weiRightB += (all[i].x - 1.5) * all[i].w;

	}
	if (weiLeftA > weiRightA)
		return 0;
	if (weiLeftB < weiRightB)
		return 0;
	return 1;

}

int cmp(const void*a, const void*b) {
	Pack *aa = (Pack*) a;
	Pack *bb = (Pack*) b;

	return aa->torque - bb->torque;

}

int main() {
	int cases = 1;
	while (scanf("%d%d%d", &len, &wei, &n)) {
		if (len == 0)
			break;
		lenL = lenR = lenM = lenA = 0;
		int i;
		int tmpX, tmpW;
		for (i = 0; i < n; i++) {
			scanf("%d%d", &tmpX, &tmpW);
			all[lenA].x = tmpX;
			all[lenA].w = tmpW;
			lenA++;

			if (tmpX < -1.5) {
				left[lenL].x = tmpX;
				left[lenL].w = tmpW;
				left[lenL].torque = abs(tmpX * tmpW);
				lenL++;
			} else if (tmpX > 1.5) {
				right[lenR].x = tmpX;
				right[lenR].w = tmpW;
				right[lenR].torque = abs(tmpX * tmpW);
				lenR++;
			} else {
				mid[lenM].x = tmpX;
				mid[lenM].w = tmpW;
				mid[lenM].torque = abs(tmpX * tmpW);
				lenM++;
			}

		}

		printf("Case %d:\n", cases++);

		if (!isBalanced(lenA)) {
			printf("Impossible\n");
			continue;
		}

		qsort(left, lenL, sizeof(Pack), cmp);
		qsort(mid, lenM, sizeof(Pack), cmp);
		qsort(right, lenR, sizeof(Pack), cmp);

		int l = 0, m = 0, r = 0, a = 0;
		int f1 = 0;
		int f2 = 0;
		int impossible = 0;

		while (m < lenM) {
			memcpy(&all[a++], &mid[m++], sizeof(Pack));
		}

		while (a < lenA) {
			while (l < lenL) {
				memcpy(&all[a++], &left[l++], sizeof(Pack));
				if (!isBalanced(a)) {
					a--;
					l--;
					f1 = 1;
					break;
				} else {
					f2 = 0;
				}
			}

			while (r < lenR) {
				memcpy(&all[a++], &right[r++], sizeof(Pack));
				if (!isBalanced(a)) {
					a--;
					r--;
					f2 = 1;
					break;
				} else {
					f1 = 0;
				}
			}
			if (f1 && f2) {
				impossible = 1;
				break;
			}

		}

		if (impossible)
			printf("Impossible\n");

		else {
			for (i = lenA - 1; i >= 0; i--)
				printf("%d %d\n", all[i].x, all[i].w);
		}

	}

	return 0;
}

 

 

枚举回溯超时的代码:

#include<stdio.h>
#include<string.h>

int len;
int wei;
int n;
int pac[25][2];
int removed[25];
int a[25];
int solved;

int isBalanced() {
	int weiLeft = 0;
	int weiRight = 0;

	weiLeft += 0.5 * (0.5 * len - 1.5) * (0.5 * len - 1.5) * wei / len;
	weiRight += 0.5 * (0.5 * len + 1.5) * (0.5 * len + 1.5) * wei / len;
	int i;
	for (i = 0; i < n; i++) {
		if (removed[i])
			continue;

		if (pac[i][0] < -1.5) {
			weiLeft += (pac[i][0] * (-1) - 1.5) * pac[i][1];
		}
		if (pac[i][0] > -1.5) {
			weiRight += (pac[i][0] + 1.5) * pac[i][1];
		}

	}
	if (weiLeft > weiRight)
		return 0;

	weiLeft = 0;
	weiRight = 0;
	weiLeft += 0.5 * (0.5 * len + 1.5) * (0.5 * len + 1.5) * wei / len;
	weiRight += 0.5 * (0.5 * len - 1.5) * (0.5 * len - 1.5) * wei / len;
	for (i = 0; i < n; i++) {
		if (removed[i])
			continue;

		if (pac[i][0] > 1.5) {
			weiRight += (pac[i][0] - 1.5) * pac[i][1];
		}
		if (pac[i][0] < 1.5) {
			weiLeft += (1.5 - pac[i][0]) * pac[i][1];
		}

	}
	if (weiRight > weiLeft)
		return 0;

	return 1;

}

void removePac(int cur) {
	if (solved)
		return;
	if (cur == n) {
		solved = 1;
		int i;
		for (i = 0; i < n; i++) {
			printf("%d,%d\n", pac[a[i]][0], pac[a[i]][1]);

		}
	} else {
		int i;
		for (i = 0; i < n; i++) {
			int j;
			int ok = 1;
			for (j = 0; j < cur; j++) {
				if (a[j] == i) {
					ok = 0;
					break;
				}
			}
			if (ok) {
				if (!isBalanced())
					return;
				a[cur] = i;
				removed[i] = 1;
				removePac(cur + 1);
				removed[i] = 0;
			}
		}

	}
	return;
}

int main() {
	int cases = 1;
	while (scanf("%d%d%d", &len, &wei, &n)) {
		if (len == 0)
			break;
		memset(removed, 0, sizeof(removed));
		solved = 0;
		int i;
		for (i = 0; i < n; i++) {
			scanf("%d%d", &pac[i][0], &pac[i][1]);
		}

		printf("Case %d:\n", cases++);
		removePac(0);
		if (!solved)
			printf("Impossible\n");
	}

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值