Description
给出若干2x2x和−2x−2x,每个数字可以用多次,要求用最少的数字使得其和为ss
Input
第一行一整数表示用例组数,每组用例首先输入一整数nn表示二进制位数,之后输入一个为nn的串表示ss串(从低位到高位输入),最后输入两个长度为的0101串a,ba,b分别表示不能用的2x2x和−2x−2x
(1≤T≤1000,1≤n≤105,∑n≤106)(1≤T≤1000,1≤n≤105,∑n≤106)
Output
输出所用数字最少的数量,保证有解且答案不会超过109109
Sample Input
3
6
110010
110101
011111
9
100101110
011111111
111111111
5
11111
00000
00000
Sample Output
3
233
2
Solution
以dp[i][0/1]dp[i][0/1]表示以加/减的方式得到ss的前位所需要的最少数字数,以add,subadd,sub分别表示用前ii位的和−2x−2x得到当前位的2i2i所需的最少数字个数,那么有:
若s[i]=0s[i]=0,那么dp[i][0]=min(dp[i−1][0],dp[i−1][1]+add)dp[i][0]=min(dp[i−1][0],dp[i−1][1]+add),即要么直接构造前i−1i−1位,要么构造前i−1i−1位取反的值,再用2i2i减掉,这需要多出addadd次运算,且有dp[i][1]=min(dp[i][1],dp[i−1]+sub)dp[i][1]=min(dp[i][1],dp[i−1]+sub),即构造前i−1i−1位取反的值再减掉2i2i,这需要多出subsub次运算
若s[i]=1s[i]=1,那么dp[i][1]=min(dp[i−1][1],dp[i−1][0]+sub)dp[i][1]=min(dp[i−1][1],dp[i−1][0]+sub),即要么直接构造前i−1i−1位取反,要么构造前i−1i−1位,再减掉2i2i,这需要多出subsub次运算,且有dp[i][0]=min(dp[i][0],dp[i−1][1]+add)dp[i][0]=min(dp[i][0],dp[i−1][1]+add),即构造前i−1i−1位然后加上2i2i,这需要多出addadd次运算
最后min(dp[n][0],dp[n][1]+1)min(dp[n][0],dp[n][1]+1)即为答案,时间复杂度O(n)O(n)
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 100005
const int INF=1e9;
int T,n,dp[maxn][2];
char s[maxn],a[maxn],b[maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s%s%s",s+1,a+1,b+1);
for(int i=0;i<=n;i++)dp[i][0]=dp[i][1]=INF;
dp[0][0]=0;
int add=INF,sub=INF;
for(int i=1;i<=n;i++)
{
if(a[i]=='0')add=1;
else add=min(2*add,INF);
if(b[i]=='0')sub=1;
else sub=min(2*sub,INF);
if(s[i]=='0')
{
dp[i][0]=min(dp[i-1][0],dp[i-1][1]+add);
dp[i][1]=min(dp[i][1],dp[i-1][1]+sub);
}
else
{
dp[i][1]=min(dp[i-1][1],dp[i-1][0]+sub);
dp[i][0]=min(dp[i][0],dp[i-1][0]+add);
}
}
printf("%d\n",min(dp[n][0],dp[n][1]+1));
}
return 0;
}