2020 百度之星 复赛 1002 Binary Addition
题目
http://acm.hdu.edu.cn/showproblem.php?pid=6839
题意
给你一串无限长的01串S、T,其中第n+1位及以后都是0。现你有两种操作作用于S串:
1、将某一位0变成1,1变成0.
2、将其视为一个数,对它+1。其中最低位在最左边
求最小的操作次数,使得S串变成T串。
题解
可以知道+1的操作最多使用一次,那么什么时候进行+1操作可以得到最小解呢?
可以for循环每一位都进行+1操作,最后维护他的最小值。
我们考虑每一位的时候都是考虑 前i+1位的变化。
11111
00000
考虑第1位+1的情况,是变成这样的,操作数是6
把前i位0变成1
11111
执行+1操作,那么理想情况前i位都会变成0,第i+1位会变成1
00111
因为s的第i+1位本来就是1,所以操作数+1
01111
因为t的第i+1位是0,不符合s的理想情况,所以操作数+1,把s的第i+1变回0
00111
最后再加上从第i+2位到第n位,s和t一共有多少不同的数就可以了
00011
00001
00000
通过这个例子我们可以发现,其实当s[i+1]==1&&t[i+1]==0的时候步骤都是多余的
所以我们再实际操作的时候可以跳过s[i+1]==1&&t[i+1]==0的时候.
用num0[i]记录S的第i位之前有多少个0,在进行+1操作之前,要把S所有的0都变成1。
用num1[i]记录T的第i位之前有多少个1,在进行+1操作之后,要把前i位的都变成1。
因为+1操作之后会进一位,所以我们还要判断下一位
如果s[i+1]==1 ,那进位操作会把它变成0,所以我们要把它变回来,操作数就+1,
如果t[i+1]==0,那么和s[i+1]不相等,操作数也要+1。
最后再加上从第i+2位到第n位,s和t一共有多少不同的数就可以了。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define PI acos(-1)
const int maxn = 1e5+5;
const ll mod = 998244353;
const double eps = 1e-6L;
char s[maxn],t[maxn];
int num0[maxn],num1[maxn],cnt[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
scanf("%s",&s);
scanf("%s",&t);
t[n]= '0';
for(int i=0;i<n;i++)
{
if(i==0)
{
num0[i] = -s[i]+'1';
num1[i] = t[i]-'0';
}
else
{
num0[i] = num0[i-1]-s[i]+'1';
num1[i] = num1[i-1]+t[i]-'0';
}
}
cnt[n]=0;cnt[n+1]=0;
for(int i=n-1;i>=0;i--)
{
cnt[i] = cnt[i+1]+(s[i]==t[i]?0:1);
}
int ans = cnt[0];//全部都执行更改操作
for(int i=0;i<n;i++)//前i位的0都变成1,然后进行+1操作
{
if(s[i+1]=='1'&&t[i + 1] == '0') continue;
int sum = 1+num0[i]+(s[i+1]=='1')+num1[i]+(t[i + 1] == '0') + cnt[i + 2];
// printf("sum = %d t[i+1] = %d\n",sum,t[i + 1] == '0');
ans = min(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}
/*
5
5
11111
00000
5
10100
01010
5
00000
00001
7
1111110
0100001
7
0011110
0100001
*/