HDU 6436 Problem K. Pow2(dp)

Description

给出若干 2x 2 x 2x − 2 x ,每个数字可以用多次,要求用最少的数字使得其和为 s s

Input

第一行一整数 T T 表示用例组数,每组用例首先输入一整数 n n 表示 s s 二进制位数,之后输入一个为 n n 01 01 串表示 s s 串(从低位到高位输入),最后输入两个长度为 n n 01 01 a,b a , b 分别表示不能用的 2x 2 x 2x − 2 x

(1T1000,1n105,n106) ( 1 ≤ T ≤ 1000 , 1 ≤ n ≤ 10 5 , ∑ n ≤ 10 6 )

Output

输出所用数字最少的数量,保证有解且答案不会超过 109 10 9

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] d p [ i ] [ 0 / 1 ] 表示以加/减的方式得到 s s 的前 i i 位所需要的最少数字数,以 add,sub a d d , s u b 分别表示用前 i i 位的 2x 2 x 2x − 2 x 得到当前位的 2i 2 i 所需的最少数字个数,那么有:

s[i]=0 s [ i ] = 0 ,那么 dp[i][0]=min(dp[i1][0],dp[i1][1]+add) d p [ i ] [ 0 ] = m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + a d d ) ,即要么直接构造前 i1 i − 1 位,要么构造前 i1 i − 1 位取反的值,再用 2i 2 i 减掉,这需要多出 add a d d 次运算,且有 dp[i][1]=min(dp[i][1],dp[i1]+sub) d p [ i ] [ 1 ] = m i n ( d p [ i ] [ 1 ] , d p [ i − 1 ] + s u b ) ,即构造前 i1 i − 1 位取反的值再减掉 2i 2 i ,这需要多出 sub s u b 次运算

s[i]=1 s [ i ] = 1 ,那么 dp[i][1]=min(dp[i1][1],dp[i1][0]+sub) d p [ i ] [ 1 ] = m i n ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + s u b ) ,即要么直接构造前 i1 i − 1 位取反,要么构造前 i1 i − 1 位,再减掉 2i 2 i ,这需要多出 sub s u b 次运算,且有 dp[i][0]=min(dp[i][0],dp[i1][1]+add) d p [ i ] [ 0 ] = m i n ( d p [ i ] [ 0 ] , d p [ i − 1 ] [ 1 ] + a d d ) ,即构造前 i1 i − 1 位然后加上 2i 2 i ,这需要多出 add a d d 次运算

最后 min(dp[n][0],dp[n][1]+1) m i n ( d p [ n ] [ 0 ] , d p [ n ] [ 1 ] + 1 ) 即为答案,时间复杂度 O(n) O ( n )

Code

#include
#include
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;
}

你可能感兴趣的:(HDU,dp)