当前位置: 代码迷 >> 综合 >> 【图论】【贪心】【搜索】[Atcoder Grand Contest 022]C Remainder Game
  详细解决方案

【图论】【贪心】【搜索】[Atcoder Grand Contest 022]C Remainder Game

热度:42   发布时间:2023-09-27 07:43:22.0

题意:

给出一个初始数组A,以及目标数组B,现在要把A的每个值改为B中相应的值,更改的方式如下:
选择一个数x,对任意一个 A中的数aiai,可以改为ai mod xaimodx,可以不改,但代价均为2x2x
求最小代价,或无解输出-1
数组A的大小n50n≤50
ai50ai≤50


分析:

由于更改方式是取模,所以必然不可能先选择一个小的数,再选择比它大的数,也就是说,选择的数必然是严格递减的,那么很显然一个数不可能被选择多次了。

因为代价为2x2x,很容易发现20+21+22++2x?1<2x20+21+22+……+2x?1<2x,也就是说,如果选择了这个数,就必然满足:选择所有比它小的数都无法得到B数组。

根据这个性质,我们可以贪心地计算了。
初始状态下,所有数都在我们选中的数集SS中(即包含从2到50所有整数)
对每个点 a i ,向ai mod SiaimodSi连一条有向边。
判断每个A中的数,是否能够到达相应的B中的数即可,判断方式可以暴搜。

然后,在S中删去当前最大的数,重新建图,判断是否能够满足,如果可以,则删去最大的数,否则把它加回来。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define SF scanf
#define PF printf
#define MAXN 60
using namespace std;
typedef long long ll;
bool used[MAXN],vis[MAXN];
ll ans;
int A[MAXN],B[MAXN],n;
bool w[MAXN][MAXN];
void build(){for(int i=1;i<=50;i++)if(used[i]==1){for(int j=0;j<=50;j++)w[j][j%i]=1;}
}
void dfs(int x){vis[x]=1;for(int i=0;i<=50;i++)if(w[x][i]==1&&vis[i]==0)dfs(i);
}
bool check(){for(int i=0;i<n;i++){memset(vis,0,sizeof vis);dfs(A[i]);if(vis[B[i]]==0)return 0;}return 1;
}
int main(){SF("%d",&n);for(int i=0;i<n;i++)SF("%d",&A[i]);for(int i=0;i<n;i++)SF("%d",&B[i]);for(int i=1;i<=50;i++)used[i]=1;build();if(check()==0){PF("-1\n");return 0;}for(int i=50;i>=0;i--){memset(w,0,sizeof w);used[i]=0;build();if(check()==0){used[i]=1;ans+=(1ll<<i);}}PF("%lld",ans);
}
  相关解决方案