• 1111: [POI2007]四进制的天平Wag


    1111: [POI2007]四进制的天平Wag

    链接

    题意:

      用一些四进制数,相减得到给定的数,四进制数的数量应该尽量少,满足最少的条件下,求方案数。

    分析:

      这道题拖了好久啊。

      参考Claris的博客。

    首先将四进制数转化为四进制数。

    一种的可行构造方案是四进制数上每一位的和。例如:$(003)_4$可以有3个$4^0$的砝码组成,当然也可以向前一位借位,$(010)_4-(001)_4$,此时就需要2个砝码了。

    所以可以推出:每位最多借一位,最高位最高是n+1位。所以可以dp表示当前是否借位过。

    f[i]表示到第i位,不向i+1借位的数字最少的个数,以及方案数。g[i]表示向i+1借位。即i+1位的一个数变成4个i位的数字,然后做差。(此处不管是否向高位借位,都不记录高位的贡献)

    $f[i] = merge(f[i - 1] + b[i] ,g[i - 1] + b[i] + 1)$

    $g[i] = merge(f[i - 1] + 4 - b[i], g[i - 1] + 3 - b[i])$

    第一个转移方程表示当前这一位不向高位借位:

      1、如果低位也不向它借位,低位自己的答案是f[i-1],那么只需要b[i]个$4^i$的砝码即可组成这一位的答案。

      2、如果低位向它借位,低位的答案g[i-1],本来b[i]个即可满足,现在需要再增加一个给低位。例如:样例$(2212)_4$到第二位时,如果第一位向它借位,那么第一位的砝码是4-1-1=2,这一位的砝码是4,所以共3个。

    第二个转移方程表示当前这一位不向高位借位:

    • 1、如果低位不向它借位,同样加上f[i-1],而它需要4-b[i]个重为$4^i$的砝码。

    • 2、如果低位向它借位,同样加上g[i-1],它需要3-b[i]个重为$4^i$的砝码,其中一个给了下一位了,那么此时是否还满足呢?样例$(2212)_4$到第二位时,借位后是16-4-4-4=4,下一位也要借位是4-1-1=2,中间其实可以消掉一个,二式相加得到16-4-4-1-1=6,刚好满足前两位的和是6

    代码:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<cmath>
     6 #include<cctype>
     7 #include<set>
     8 #include<queue>
     9 #include<vector>
    10 #include<map>
    11 #include<bitset>
    12 using namespace std;
    13 typedef long long LL;
    14 
    15 inline int read() {
    16     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    17     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
    18 }
    19 
    20 const int N = 1670;
    21 char s[N];
    22 int a[N], b[N];
    23 struct Node{
    24     int x, y;
    25     Node() {}
    26     Node(int _x,int _y) { x = _x, y = _y; } 
    27     Node operator + (int a) { return Node(x + a, y); }
    28     Node operator + (Node A) {
    29         if (x == A.x) return Node(x, (y + A.y) % 1000000000);
    30         return x < A.x ? *this : A;
    31     }
    32 }f[N], g[N];
    33 
    34 int main() {
    35     scanf("%s", s + 1);
    36     int len = strlen(s + 1), n = 0;
    37     for (int i = 1; i <= len; ++i) a[i] = s[len - i + 1] - '0';
    38     
    39     while (len) { // 分解为四进制数,每次找到模4剩下的余数 
    40         a[0] = 0;
    41         for (int i = len; i; --i) 
    42             a[i - 1] += (a[i] & 3) * 10, a[i] >>= 2; // 第i位模4后的余数,到下一位乘10 
    43         b[++n] = a[0] / 10; 
    44         for (; len && !a[len]; len --);
    45     }
    46     
    47     n ++;
    48     f[0] = Node(0, 1); g[0] = Node(1e9, 0);
    49     for (int i = 1; i <= n; ++i) { // 从低位往高位dp 
    50         f[i] = (f[i - 1] + b[i]) + (g[i - 1] + (b[i] + 1));
    51         g[i] = (f[i - 1] + (4 - b[i])) + (g[i - 1] + (3 - b[i]));
    52     }
    53     cout << f[n].y;
    54     return 0;
    55 }
  • 相关阅读:
    批量刷新远程物化视图的方法(备用)
    Oracle 11g中CTE应用示例
    PL/SQL DEVELOPER中查询结果复制出来中文乱码的解决方案
    RHEL5.5 64位下安装Oracle 11g 64位安装前置条件的两种方法
    不良言论屏蔽方案探讨——自说自话方案
    AWWWB 网站克隆器 v2.0发布
    OneNote中到底能放多少种东西?
    Visual Studio 2010 旗舰版 安装问题小记
    WPF命中测试示例(一)——坐标点命中测试
    网页内嵌Windows Media Player播放器的多文件播放方法
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10107435.html
Copyright © 2020-2023  润新知