HihoCoder 1527 题解

转载请注明出处,http://blog.youkuaiyun.com/Bule_Zst/article/details/77116239


题目:

When we are coding, we can replace x×a by (x<< a0 ) op1 (x<< a1 ) op2 (x<< a2 ) … opn (x<< an ) to make the program faster where opi is + or -.

For example, you can replace x × 15 by (x<<4)-(x<<0).

In this problem we assume that operator‘+’,‘-’and ‘<<’will cost 1 unit of time and operator‘*’will cost infinity units of time. For example, (x<<4)-(x<<1) will cost 3 units of time.

Given a, what’s the minimum units of time does it take to compute the value of (x×a) ?

Input

One ‘01’ string S : the binary representation of a (from the most significant digit to the least significant digit)

1 ≤ the length of string S106

a > 0

Output

An integer : the answer

Sameple Input

1111

Sameple Output

3

题目大意:

任何一个整数都可以用一系列2的幂加减得到,加、减和幂次运算耗时为1,给定一个整数(以二进制字符串形式给出),问最短耗时

方法:

1.二幂拆分问题,详见这篇博客

直接给出代码:

// @Team    : nupt2017team12
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
#define MOD 1000000007
#define CLR(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define pb push_back
#define FOR(i,a,b) for( int i = ( a ); i <= ( b ); ++i )

const int N = 1e6;

int len;
char str[N];

int u, v;


int main()
{
    // freopen( "E.txt", "r", stdin );
    scanf( "%s", str );
    len = strlen( str );
    int index = -1;
    for( int i = len-1; i >= 0; i-- ) {
        if( str[i] == '1' ) {
            index = i;
            break;
        }
    }
    u = v = 1;
    for( int i = index-1; i >= 0; i-- ) {
        if( str[i] == '1' ) {
            u = min( u, v ) + 1;
        } else {
            cout<<u<<" "<<v<<endl;
            v = min( u, v ) + 1;
            cout<<v<<endl;
        }
    }
    printf( "%d\n", 2 * u - 1 );

    return 0;
}

2.动态DP(其实第一种方法,本质上也是DP)

先将给的字符串倒置,然后从左往右扫

  • 如果遇到‘0’则ans[i] = ans[i-1]
  • 如果遇到‘1’则有两种可能,ans[i]取两种可能中的较小值
    • 第一种,ans[i] = ans[i-1] + 1,直接加上扫到的1
    • 第二种,ans[i] = ans[j-1] + 2 - ( zero[i] - zero[j-1] )

      2表示,在str[j-1]的基础上,先加 2i+1 ,再减去 2j ,这样,原先的x就会变成一个 在二进制表示下,从j位到i位全是1的新数。(i、j从0开始数)(str[n]表示前n位二进制串组成的数)

      x=(100110)2 ,则 (10)2+25+122=(111110)2

      zero[n]表示二进制串前n位中,0的个数。减去( zero[i] - zero[j-1] ),就是减去原先应该是0的位。

代码:

// @Team    : nupt2017team12
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
#define MOD 1000000007
#define CLR(a,x) memset(a,x,sizeof(a))
#define INF 0x3f3f3f3f
#define pb push_back
#define FOR(i,a,b) for( int i = ( a ); i <= ( b ); ++i )

const int N = 1e6+7;
char str[N];
int len;
int zero[N];
int ans[N];


int main()
{
    // freopen( "E.txt", "r", stdin );
    while( scanf( "%s", str ) != EOF ) {
        CLR( zero, 0 );
        CLR( ans, 0 );

        len = strlen( str );
        reverse( str, str+len );
        zero[0] = ( str[0] == '0' );
        FOR( i, 1, len-1 ) {
            zero[i] = zero[i-1] + ( str[i] == '0' );
        }
        int j = 0;
        ans[0] = ( str[0] == '1' );
        FOR( i, 1, len-1 ) {
            if( str[i] == '0' ) {
                ans[i] = ans[i-1];
            } else {
                ans[i] = ans[i-1]+1;
                if( j == -1 ) {
                    ans[i] = min( ans[i], 2 - zero[i] );
                } else {
                    ans[i] = min( ans[i], ans[j-1] + 2 + ( zero[i] - zero[j-1] ) );
                }
            }
            // 对于i+1位更新j
            if( j == -1 ) {
                if( 2 - zero[i+1] >= ans[i-1] + 2 + ( zero[i+1] - zero[i-1] ) ) 
                    j = i;
            } else {
                if( ans[j-1] + 2 + ( zero[i+1] - zero[j-1] ) >= ans[i-1] + 2 + ( zero[i+1] - zero[i-1] ) )
                    j = i;
            }
            cout<<j<<endl;
        }
        printf( "%d\n", ans[len-1] * 2 - 1 );
    }

    return 0;
}

参考博客:http://blog.youkuaiyun.com/werkeytom_ftd/article/details/73740918

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值