Atcoder Grand Contest 022 C
2018-04-03 01:42
411 查看
Atcoder Grand Contest 022 C
C题
题意
希望把给定的一个序列转化为另一个序列,每次操作可以选择对任意一个元素求余数或不求,每次操作的花费为2^k求,求转化的最小花费。笺释
关键在于转变思路,我们并不是要把一个序列一步步地转化为另一个序列,这样的话必然设计到记录每一个数字的数值作为一个状态,而记录这样的状态是不可能做到的。我们要做的是枚举余数,因为数字最大是50,所以余数也最大是50(其实是49吧喂!),可以通过二进制的思想将每一个余数用与没用作为一位保留在一个数A中,判断使用这些数字能否由A转化为B。
二进制的写法见详细代码,这里说一下判断能否转化的方法。
for(int i=1;i<=n;i++) { if(a[i]==b[i]) { continue; } flag=0; memset(dp,0,sizeof(dp)); dp[a[i]]=1; for(int j=a[i];j>=0;j--) { //注意两个内层循环一定是从a[i]向0,并且0一定能取到。 //在第一个循环中0代表着余数1的用与不用,在第二个循环中0代表着转化为的一个数0 //逆序是因为要让dp顺利完成更新(每次取余所得结果一定变小) if(((1ll<<j)&ans)>0) { // printf(" a %d\n",j); for(int k=a[i];k>=0;k--) { if(dp[k]) { dp[k%(j+1)]=1; if((k%(j+1))==b[i]) { flag=1; } } } } } if(!flag) { // printf("%d\n",i); return 0; } } return 1;
还有一种更快的方法,从50向1,每次去掉一个余数,当一个余数必须使用的时候将其保留,然后更新状态。
详见完整代码
完整代码
#include<bits/stdc++.h> #define MAXN 50 using namespace std; typedef long long ll; int n,flag; int a[MAXN+1],b[MAXN+1]; int dp[MAXN]; long long ans; bool solve(ll ans) { for(int i=1;i<=n;i++) { if(a[i]==b[i]) { continue; } flag=0; memset(dp,0,sizeof(dp)); dp[a[i]]=1; for(int j=a[i];j>=0;j--) { if(((1ll<<j)&ans)>0) { // printf(" a %d\n",j); for(int k=a[i];k>=0;k--) { if(dp[k]) { dp[k%(j+1)]=1; if((k%(j+1))==b[i]) { flag=1; } } } } } if(!flag) { // printf("%d\n",i); return 0; } } return 1; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { scanf("%d",&b[i]); } ans=(1ll<<MAXN)-1; if(!solve(ans)) { printf("-1"); return 0; } for(int i=MAXN-1;i>=0;i--) { //printf("%lld\n",ans); ans^=1ll<<i; if(!solve(ans)) { ans^=1ll<<i; } } printf("%lld\n",ans<<1); }
#include<bits/stdc++.h> #define MAXN 50 using namespace std; int n; int a[MAXN+1],b[MAXN+1]; bool solve[MAXN+1][MAXN+1][MAXN+1]; bool dp[MAXN+1][MAXN+1]; int main() { scanf("%d",&n); for(int i=< 4000 span class="hljs-number">1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { scanf("%d",&b[i]); } memset(solve,false,sizeof(solve)); memset(dp,false,sizeof(dp)); for(int i=0;i<=MAXN;i++) { dp[i][i]=true; solve[0][i][i]=true; } for(int i=1;i<=MAXN;i++) { for(int j=0;j<=MAXN;j++) { for(int k=0;k<=MAXN;k++) { solve[i][j][k]=solve[i-1][j][k]; } } for(int j=0;j<=MAXN;j++) { for(int k=0;k<=MAXN;k++) { if(solve[i][j%i][k]) { solve[i][j][k]=true; } } } } for(int i=1;i<=n;i++) { if(!solve[MAXN][a[i]][b[i]]) { printf("%d\n",-1); return 0; } } long long ans=0; for(int i=MAXN;i>=1;i--) { int j,k; //这一段的精髓之处在于这样的转化关系:想要由a变成b //一定可以看作由a变为一个中间变量k变到b的(k可能等于a) //在当前已经取了一定量的余数的情况下,dp[a[j]][k]存在意味着dp可以变到k //而只要存在中间变量k能够使a[j]变到k变到b[j]即可 //这实际上是包含了a[j]->A->B->C->D->b[j]这种多个中间变量的情况的 for(j=1;j<=n;j++) { for(k=0;k<=MAXN;k++) { if(dp[a[j]][k]) { if(solve[i-1][k][b[j]]) { // printf("ss\n"); break; } } } //每一个都没走完MAXN个,可以跳过 就continue //有一个走完了MAXN个,不能跳过,就不continue if(k>MAXN) { // printf("A %d\n",j); break; } } if(j==n+1) { continue; } ans+=1ll<<i; // printf("%d\n",i); for(j=1;j<=MAXN;j++) { for(k=1;k<=MAXN;k++) { if(dp[j][k]) { dp[j][k%i]=true; } } } } printf("%lld\n",ans); }
相关文章推荐
- Atcoder Grand Contest 022 B 题解
- AtCoder Grand Contest 022
- AtCoder Grand Contest 022C: Remainder Game 题解
- AtCoder Grand Contest 011
- AtCoder Grand Contest 012 A - AtCoder Group Contest(贪心)
- Atcoder Grand Contest 016F - Games on DAG
- AtCoder Grand Contest 018 D - Tree and Hamilton Path 树的重心
- AtCoder Grand Contest 011 D - Half Reflector 乱搞
- AtCoder Grand Contest 002 D - Stamp Rally 并查集+二分答案+倍增
- AtCoder Grand Contest 004 F - Namori 贪心+构造
- [AtCoder Grand Contest 018] D: Tree and Hamilton Path (agc018D)
- AtCoder Grand Contest 021C-Tiling
- (技巧)AtCoder Grand Contest 018 C : Coins
- AtCoder Grand Contest 016 D - XOR Replace
- AtCoder Grand Contest 6
- AtCoder Grand Contest 002 D - Stamp Rally
- AtCoder Grand Contest 020 B - Ice Rink Game(贪心+计算机乘法)
- Atcoder Grand Contest 011F - Train Service Planning
- AtCoder Grand Contest 015 C - Nuske vs Phantom Thnook 乱搞
- AtCoder Grand Contest 011 E - Increasing Numbers 乱搞+高精度