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 }