Loading

题解 【P4026 [SHOI2008]循环的债务】

\(\Large\texttt{P4026 [SHOI2008]循环的债务}\)

萌新不会 DP 捏,只会暴力 DFS。

可能是数据水了,目前最优解第一 130ms,没有卡常。

思路

发现题目约束三人总面值之和不会超过 1000 元,并且对每人小于 10 元的货币个数进行了约束,所以毛咕咕 1 元的货币的个数比较多。

因为我们大力 DFS,枚举每个人最终最优情况中每种面值大于 1 元的货币的个数,最后判断可行性并用 1 元的货币调平。

对于每种货币之间的交换,因为互相独立,对于原来一种货币三人分别有 \(a0,b0,c0\) 个,最终每人分别有 \(a1,b1,c1\) 个,最优交换次数一定是 \(\frac{|a0 - a1| + |b0 - b1| + |c0 - c1|} {2}\),贪心容易证明。

然后就做完了,记得 DFS 加点可行性和最优性剪枝。

代码

int a, b, c, s[3], p[3][6], q[3][6], mx[6], t[3], o[6] = {100, 50, 20, 10, 5, 1}, ans = inf;

int calc() {
	int tmp = 0;
	rep(i, 0, 2) {
		t[i] = s[i];
		rep(j, 0, 4) t[i] += o[j] * (p[i][j] - q[i][j]);
		if(t[i] < 0 && abs(t[i]) > p[i][5]) return inf;
		tmp += abs(t[i]);
	}
	return tmp / 2;
}

int upd(int n) {
	return (abs(p[0][n] - q[0][n]) + abs(p[1][n] - q[1][n]) + abs(p[2][n] - q[2][n])) / 2;
}

void dfs(int n, int now) {
	if(now >= ans) return;
	if(n == 5) {
		ans = min(ans, now + calc());
		return;
	}
	rep(i, 0, mx[n]) rep(j, 0, mx[n] - i) {
		q[0][n] = i, q[1][n] = j, q[2][n] = mx[n] - i - j;
		dfs(n + 1, now + upd(n));
	}
}

signed main() {
	// freopen("in1.in", "r", stdin);
	a = read();
	b = read();
	c = read();
	s[0] = -a + c;
	s[1] = -b + a;
	s[2] = -c + b;
	rep(i, 0, 2) rep(j, 0, 5) p[i][j] = read(), mx[j] += p[i][j];
	dfs(0, 0);
	if(ans == inf) cout << "impossible";
	else cout << ans;
	return 0;
}
posted @ 2021-11-06 21:49  RedreamMer  阅读(36)  评论(0编辑  收藏  举报