您的位置:首页 > 其它

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: