LG15208 [NWERC 2025] Bisecting Bargain 题解

LG15208 [NWERC 2025] Bisecting Bargain 题解

取款机可支付任意由面值 \(1,2,5,10,20,50,100,200,500\) 组成的金额,总和为 \(n\)。给定 \(n\)\(1\le n\le10000\)),判断是否无论取款机如何支付,金额总能均分为两堆各 \(n/2\)。若是,输出 splittable;否则输出一组总和为 \(n\) 且无法均分的硬币/纸币:先输出数量,再输出各面值(任意一组即可)。

给一种最暴力也是最短的做法

首先可以得到观察:

  • \(1,5,10,50,100\) 的钞票在最终的分配方案中至多出现 \(1\) 次,否则每对于两张面值为 \(1,5,10,50,100\) 的钞票,不如改为选取一张 \(2,10,20,100,200\) 的钞票。
  • \(2,20\) 元的钞票在最终的分配方案中至多出现 \(4\) 次,否则每对于五张面值为 \(2,20\) 的钞票,不如改为选取一张 \(10,100\) 的钞票。

我们不管 \(200,500\) 的钞票有没有更优秀的性质,由于 \(n\leq 10000\)\(200\) 的最多选取 \(50\) 张,\(500\) 的最多选取 \(20\) 张。

综上,我们至多只用考虑 \(2^5\times 5^2\times 51\times 21\approx 8.6\times 10^5\) 种情况。实际上完全达不到这个上界。


然后我们在枚举的过程中用背包统计当前的方案能否取到某个值,即定义 \(f_i=1/0\) 表示当前的选取方案能或否组成 \(i\) 的面额,最后如果 \(f_{n/2}=0\) 则表明无法平均分配。

假设当前考虑一张面值为 \(v\) 的钞票加入到方案中,设新方案的背包状态为 \(g\),有转移

\[\quad \forall i,g_{i+v}=\min(1,f_i+f_{i+v}) \]

相当于 \(f_i\)\(f_{i+v}\) 有一个为真,则 \(g_{i+v}\) 为真,这是的关系,且对于每个 \(i\) 都有这样的关系,考虑使用 std::bitset 优化这个背包转移。可写作

g=f|(f<<v);

于是单次转移的复杂度为 \(O(\frac{n}{w})\)。计算得总的运行上界约为 \(1.3\times 10^8\) 次计算。实际加上剪枝后跑得飞快,单点最慢 \(6\) 毫秒;而且不加剪枝这个实际也无法取到这个上界,每个点也不超过 \(200\) 毫秒。

#include <bits/stdc++.h>

using namespace std;

const int N=10004;

const int v[10]={0,1,2,5,10,20,50,100,200,500};
const int r[10]={0,1,4,1, 1, 4, 1,  1, 50, 20};

int n;

int stk[N],top;
void dfs(int i,int s,bitset<N>&f){
	bitset<N>g=f;
	if(s==n){
		if(!f[n/2]||n%2==1){
			printf("%d\n",top);
			for(int i=1;i<=top;++i){
				printf("%d ",stk[i]);
			}puts("");
			exit(0);
		}
	}
	if(s>n||i>9)return;
	if(i>=7&&(s%100)!=(n%100))return;//考虑最后枚举200和500时若个十位不同则无法满足,剪枝
	for(int j=0;j<=r[i];++j){
		for(int k=1;k<=j;++k)stk[++top]=v[i];
		dfs(i+1,s+j*v[i],f);
		top-=j;
		f=f|(f<<v[i]);
	}
	f=g;
}

int main(){
	scanf("%d",&n);
	bitset<N> tmp;tmp[0]=1;
	dfs(1,0,tmp);
	puts("splittable");
	return 0;
}

最后正如官方题解所言,此题考虑暴力优化比贪心会更加容易一些。

posted @ 2026-09-16 17:35  BigSmall_En  阅读(64)  评论(0)    收藏  举报