zoukankan      html  css  js  c++  java
  • HihoCoder 1527 动态规划,贪心

    https://hihocoder.com/problemset/problem/1527

    时间限制:20000ms

    单点时限:1000ms

    内存限制:256MB

    描述

    在写代码时,我们经常要用到类似 x × a 这样的语句( a 是常数)。众所周知,计算机进行乘法运算是非常慢的,所以我们需要用一些加法、减法和左移的组合来实现乘一个常数这个操作。具体来讲, 我们要把 x × a 替换成:(x<<a0) op1 (x<<a1) op2 (x<<a2) ... opn (x<<an) 这样的形式,其中opi 是+或者-。

    举个例子:x × 15 = (x<<4) - (x<<0)。

    在本题中,假设左移(包括左移0)和加法、减法所需要的时间都是一个单位的时间,上述的例子所需要的时间是3。

    现在给定常数 a 的二进制形式,求实现 x × a 最少需要多少单位的时间。

    输入

    一个01串,表示 a 的二进制形式,从左到右分别是从高位到低位。

    0 < 01串的长度之和 ≤ 106

    a > 0。

    输出

    输出一个数,表示最小需要多少单位的时间可以实现 x × a

    样例输入 1111样例输出 3

    错解贪心选择超过两个连续的1的决策不如使用高位减低位的方法,110不如1<<3 – 1<<1然后,单个1选择直接加上。如果两个大块(2个连续1以上)中间之隔为1个0,那么可以合并,110110不如 1<<6 – 1<<4 + 1<<3 – 1<<1 = 1<<6 – 1 <<3 – 1<<1

    是我太弱,所以没有贪出来,少考虑了一种情况就是对于类似1101011这样,可以抉择把0填满然后变成全1,用1<<7-1<<0.

    错误代码:

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <string>
    #include <math.h>
    using namespace std;
    typedef pair<int,int> P;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int N = 1e6 + 5;
    
    char s[N];
    
    int main()
    
    {
        while(scanf("%s", s) != EOF)
        {
            int n = strlen(s);
            int cnt = 0;
            int i = 0;
            while(i < n)
            {
                int k = 0;
                while(i < n && s[i] == '1')
                {
                    k++;
                    i++;
                }
                if(k == 1) cnt++;
                else if(k >= 2) cnt += 2;
                if(k >= 2 && i+2 < n && s[i+1] == '1' && s[i+2] == '1')
                    cnt -= 1;
                if(i < n && s[i] == '0')
                    i++;
            }
            printf("%d
    ", 2*cnt-1);
        }
        return 0;
    }

    AC贪心:

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <string>
    #include <math.h>
    using namespace std;
    typedef pair<int,int> P;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int N = 1e6 + 5;
    
    char s[N];
    int main()
    {
        while(scanf("%s", s) != EOF)
        {
            int n = strlen(s);
            int cnt = 0;
            int i = 0,k,flag=0;
            while(i < n)
            {
                if(!flag)
                    k = 0;
                flag = 0;
                while(i < n && s[i] == '1')
                {
                    k++;
                    i++;
                }
                if(k == 1) cnt++;
                else if(k >= 2) cnt += 2;
                if(k >= 2 && i+1 < n && s[i+1] == '1')
                {
                    cnt -= 1;
                    flag = 1;
                    s[i] = '1';
                    k++;
                }
                if(i < n && s[i] == '0')
                    i++;
            }
            printf("%d
    ", 2*cnt-1);
        }
        return 0;
    }
    题解:很多都是二幂划分的解释,也可以用dp来做
    从低位往高位考虑。dp[i]表示第i位的时候需要进行最少操作数
    如果当前位是0,dp[i] = dp[i-1]
    如果当前位是1,可以有两种选择,直接加上dp[i] = dp[i-1] + 1;
    或者,把某一部分的0全部补上,然后用高位1减去,例如01100 = 10000 – 00100,也就是 [x, i]全部都变成1,也就是要补上x~i的0
    dp[i] =min{ dp[x-1] + 2 + sum[i] – sum[x-1]}
    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <string>
    #include <math.h>
    using namespace std;
    typedef pair<int,int> P;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int N = 1e6 + 5;
    
    char s[N];
    int sum[N], dp[N];
    int main()
    {
        scanf("%s", s+1);
        int n = strlen(s+1);
        memset(dp,0,sizeof(dp));
        memset(sum, 0, sizeof(sum));
        reverse(s+1,s+1+n);
        for(int i = 1; i <= n; i++) sum[i] =sum[i-1] + (s[i] == '0');
        dp[0] = 0;
        int mx = -1;
        for(int i = 1; i <= n; i++)
        {
            if(s[i] == '0')
                dp[i] = dp[i-1];
            else
            {
                dp[i] = dp[i-1] + 1;
                if(mx > 0)
                    dp[i] = min(dp[i], dp[mx-1] + 2 + sum[i] - sum[mx-1]);
            }
            if(mx == -1 || dp[i-1] + 2 + sum[i+1] - sum[i-1] <= dp[mx-1] + 2 + sum[i+1] - sum[mx-1])
                mx = i;
        }
        printf("%d
    ", 2*dp[n]-1);
        return 0;
    
    }
    如果有错误,请指出,谢谢
  • 相关阅读:
    函数式编程笔记
    Java时间类总结
    【问题记录】MySQL中时间戳转日期格式和Java中时间戳转日期格式偶尔不一致
    Java 注解
    Java编程思想之十 内部类
    Java编程思想之九 接口
    Java编程思想之八多态
    Java编程思想之七复用类
    Java编程思想之六访问权限控制
    Java编程思想之五初始化与清理
  • 原文地址:https://www.cnblogs.com/Alruddy/p/7336983.html
Copyright © 2011-2022 走看看