专栏文章
AT_abc432_f 题解
AT_abc432_f题解参与者 4已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @min6o5py
- 此快照首次捕获于
- 2025/12/01 21:26 3 个月前
- 此快照最后确认于
- 2025/12/01 21:26 3 个月前
状压 DP。恶心点在于还要还原方案 /tuu 而且码量不小。
显然要先把所有糖果的数量总和算出来,不是 的倍数直接无解。否则算出平均值。
首先我们会发现一个特别,啊,显然的性质,就是一次糖果的传递,比如说 给 多少颗糖,经过这次操作后 和 这两个人之中一定有一个人已经到达了平均值,不排除两人都到的情况。
这个性质可以延伸出另一个性质,那就是说,一颗糖果绝对不会经过一个人的交手。这样说是不是很模糊啊,那我换种方式,就是说一个糖果最多只会拥有过两个主人,不会说 给了 , 又给了 ,那何必呢,直接让 给 不就得了。
所以根据这些性质,如果你当前只考虑了一部分的人,这之中必定最多只有一个人糖果数还没达标。
于是这样就可以定义状态, 表示当前考虑了 这个二进制压缩集合中的人的糖果情况,且还有 这位虽然经过过操作但是糖果数还没达标的人。当然了也有全部达标的情况,这个时候我们就让 这一位表示成 ,也就是目前考虑过的人中没有人的糖果数还没达标啦!
最终答案就是 。
初始时肯定全赋初值为 。找最初就糖果数达标的作为起始 ,让 ,这就是初值。
接下来考虑转移。
首先枚举 ,特殊转移第二维是 的情况——枚举 ,只要 ,就可以转移 ,顺便记录路径方便后面找方案。
接下来枚举 DP 的第二维 ,这里用的是扩散型,对,上面用的也是扩散型。再枚举一个 ,要新加一个 进来,当然 一定不能在 中或者等于 。并且还有一点很重要的,你会算出 和 两个标识符,表示 和 这两个人距离糖果数达标还需要多少。你必须保证 和 的正负号是不一样的——不然你很有可能给不起!
之后就是判断了,要是 和 正好相互补上,就能弄出 DP 转移第二维是 的情况, 就会加上 和 两个人;如果 的绝对值更小一些,就考虑让 达标,让 去成全 ,把 包含进 并让第二维是 ;反之如果 的绝对值更小一些,久石让 达标,让 成全 ,把 包含到 里头,第二维还是 。在这个转移的过程中,要记得及时记录路径,以及要判断这种转移优不优,别越弄越劣了就行。
这样转移环节就结束了,思维难度不高,但是由于有多种情况,代码还有点儿长。
最后输出答案即可,额外再跑一个 DFS 倒着回来找方案,后序遍历顺序输出方案的具体情况即可。
于是这题就搞完啦!有些小细节需要注意一下,本人补题时调了将近一个小时。
CPP#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
using namespace std;
const int N = (1<<20)+5;
const int Inf = 0x3f3f3f3f;
struct node{int x,y,z;}g[N][30];
int n,a[30],s[N],c[N],sum,strt,dp[N][30];
int read(){
int su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
void DFS_rtans(int x,int y){
if(x==strt&&y==0)return;
if(x==-1||y==-1)return;
auto [p,q,o]=g[x][y];
if(p==0)DFS_rtans(x,0);
else{
if(y==0)DFS_rtans((x^(1<<p-1)^(1<<q-1)),p);
else if(y==q)DFS_rtans(x^(1<<p-1),p);
else DFS_rtans(x^(1<<q-1),p);
if(o>0)cout<<p<<" "<<q<<" "<<o<<"\n";
else cout<<q<<" "<<p<<" "<<-o<<"\n";
}return;
}
int main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),sum+=a[i];
if(sum%n!=0){cout<<"-1\n";return 0;}sum/=n;
for(int st=0;st<(1<<n);st++)
for(int i=1;i<=n;i++)
if((st>>i-1)&1)s[st]+=a[i],c[st]++;
for(int i=1;i<=n;i++)
if(a[i]==sum)strt|=(1<<i-1);
memset(dp,0x3f,sizeof(dp));dp[strt][0]=0;
for(int st=0;st<(1<<n);st++)
for(int i=0;i<=n;i++)g[st][i]={-1,-1,-1};
for(int st=0;st<(1<<n);st++){
if(dp[st][0]!=Inf)for(int i=1;i<=n;i++)
if(!((st>>i-1)&1)&&dp[st][0]<dp[st][i])
dp[st][i]=dp[st][0],g[st][i].x=0;
for(int i=1;i<=n;i++){
if(dp[st][i]==Inf)continue;
for(int j=1;j<=n;j++){
int di=s[st|(1<<i-1)]-c[st|(1<<i-1)]*sum;
int dj=a[j]-sum,ds=min(abs(di),abs(dj));
if(((st>>j-1)&1)||((di<0)==(dj<0)))continue;
if(ds==abs(di)&&ds==abs(dj)){
if(dp[st][i]+1<dp[st|(1<<i-1)|(1<<j-1)][0])
dp[st|(1<<i-1)|(1<<j-1)][0]=dp[st][i]+1,
g[st|(1<<i-1)|(1<<j-1)][0]={i,j,di};
}else if(ds==abs(di)){
if(dp[st][i]+1<dp[st|(1<<i-1)][j])
dp[st|(1<<i-1)][j]=dp[st][i]+1,
g[st|(1<<i-1)][j]={i,j,di};
}else if(ds==abs(dj)){
if(dp[st][i]+1<dp[st|(1<<j-1)][i])
dp[st|(1<<j-1)][i]=dp[st][i]+1,
g[st|(1<<j-1)][i]={i,j,-dj};
}else;
}
}
}cout<<dp[(1<<n)-1][0]<<"\n";
DFS_rtans((1<<n)-1,0);
return 0;
}
如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...