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 }

 

posted @ 2018-12-12 11:46  MJT12044  阅读(654)  评论(0编辑  收藏  举报