P7022 [NWRRC2017] Dividing Marbles
本篇题解参考 https://www.cnblogs.com/vb4896/p/9489338.html 在此致谢。
注意拆分后得到的数会去重。因此有一个方案是假设
这个过程可以视作加法链,
然而有时候不是这样。
现在有两条性质:
考虑
接下来有几种搜索方法:
方法一:每组数据搜索,枚举
很遗憾,
1
13 17 19 20
直接能把它卡 T 飞。
方法二:枚举值域太傻,枚举当前链中元素暴力组合。由于元素是
然而
方法三:只进行一次 DFS 预处理出所有数的答案。注意
每次往链中加元素时
a_i=a_{i-1}+a_k(k<i) 一定成立。因为a_{i-1} 如果拼出来却暂时不用完全可以到后面用到再拼。
举个例子,假设现在链中元素
因此直接枚举
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
int T,d1,d2,d3,d4,n,s,t;
int a[35];
vector<int>ans[4*(1<<20)+5],v;
void dfs(int nw){
if(nw>=25||(nw>=2&&a[nw]<(1<<nw-2))) return;//24=max(s+t-3)=23+4-3
if(__builtin_popcount(a[nw])<=4&&(!ans[a[nw]].size()||ans[a[nw]].size()>nw)){//更新答案
ans[a[nw]].clear();
for(int i=0;i<=nw;i++) ans[a[nw]].pb(a[i]);
}
for(int i=0;i<=nw;i++){//扩展
a[nw+1]=a[nw]+a[i];
if(a[nw+1]<=(1<<22)) dfs(nw+1);
else break;
}
}
int main(){
scanf("%d",&T);
a[0]=1;
dfs(0);
for(int i=1;i<=(1<<22);i++){
if(__builtin_popcount(i)<=4&&!ans[i].size()){//答案为 s+t-2
for(int j=0;(1<<j)<=i;j++) ans[i].pb(1<<j);
v.clear();
int u=i;
while(u!=(u&-u)) v.pb(u),u-=(u&-u);
for(int j=v.size()-1;j>=0;j--) ans[i].pb(v[j]);//倒着插回去
}
}
while(T--){
scanf("%d%d%d%d",&d1,&d2,&d3,&d4);
n=(1<<d1)+(1<<d2)+(1<<d3)+(1<<d4);
printf("%d\n",ans[n].size()-1);
for(int i=ans[n].size()-1;i>0;i--) printf("%d %d %d\n",ans[n][i],ans[n][i-1],ans[n][i]-ans[n][i-1]);//直接相减构造答案
}
return 0;
}