[toj1003]Transportation

题意:给定0~m共m+1个城市(其中0为A城,m为B城),一列火车,容量一定。另有一些订单,每笔订单有起点,终点和人数。对于每笔订单,或者接收所有人,或者不接这笔订单,每笔收益为人数乘以乘车距离(如2 5 3,收益为3*(5-2)=9)。对于给定的block,求最大收益。

开始时没怎么想。但是数据非常小,订单数量不超过22,m不大于7.感觉暴搜可做,总共状态数是2^22≈400万,是可以接受的。但是犯了糊涂,暴搜中用了状态压缩,致使解压状态代价过大,果断T了。。。后来感觉既然中间过程比较黑,那就按点搜吧, 于是按城市编号搜,依然T。。。然后又用了一个暴力枚举,擦,感觉常数也不大啊,400万的状态,也还马马虎虎啊。。。可是还是T。。。思前想后没有结果,于是search了一下...擦,原来有一个黑剪枝:对每笔订单,预处理从其起点开始到终点最大的可能受益,中间就可以加一个剪枝了T_T 艹!于是重新写了一个姿势好一些的搜索,依然按订单搜(当然,上述一定是先把订单排序过的),加上剪枝0.05'',去掉剪枝就真相了:0.46"。。。唉,写搜索剪枝技能好烂啊!以后的练习中一定要多思考...

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX = 32;
struct Order {
	int from;
	int to;
	int flw;
	int moy;
	int maxRest;
	bool operator<(const Order& B)const {
		if (from != B.from) return from < B.from;
		if (to != B.to) return to < B.to;
		return flw < B.flw;
	}
} joke[MAX];
int flow[MAX];
int cap, n, m, ans;

inline void add(int left, int right, int addon) {
	while (left <= right) {
		flow[left++] += addon;
	}
}

void dfs(int id, int sum) {
	if (id == n) {
		if (sum > ans) ans = sum;
		return;
	}
	if (sum + joke[id].maxRest < ans) return;
	if (flow[joke[id].from + 1] + joke[id].flw <= cap) {
		add(joke[id].from, joke[id].to, joke[id].flw);
		dfs(id + 1, sum + joke[id].moy);
		add(joke[id].from, joke[id].to, -joke[id].flw);
	}
	dfs(id + 1, sum);
}

int main() {
	while (~scanf(" %d %d %d", &cap, &m, &n)) {
		if (cap == 0 && m == 0 && n == 0) break;
		for (int i = 0; i < n; ++i) {
			scanf(" %d %d %d", &joke[i].from, &joke[i].to, &joke[i].flw);
			joke[i].moy = joke[i].flw * (joke[i].to - joke[i].from);
		}
		sort(joke, joke + n);
		int sum = 0;
		for (int i = n - 1; i >= 0; --i) {
			sum += joke[i].moy;
			joke[i].maxRest = sum;
		}
		ans = 0;
		memset(flow, 0, sizeof(flow));
		dfs(0, 0);
		printf("%d\n", ans);
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值