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 }