1111: [POI2007]四进制的天平Wag
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 223 Solved: 151
[Submit][Status][Discuss]
Description
Mary准备举办一个聚会,她准备邀请很多的人参加她的聚会。并且她准备给每位来宾准备一些金子作为礼物。为了不伤及每个人的脸面,每个人获得的金子必须相同。Mary将要用一个天平来称量出金子。她有很多的砝码,所有砝码的质量都是4的幂。Mary将金子置于左边并且将砝码置于右盘或者两个盘。她希望每次称量都使用最少的砝码。并且,他希望,每次都用不同的称量方法称出相同质量的金子。对于给定的质量n,Mary希望知道最少需要用多少个砝码可以完成称量,并且想知道用这么多个砝码一共有多少种方式进行称量。
Input
输入文件仅包含一个整数,表示Mary希望给每个人的金子的质量。(1<=n<=10^1000)
Output
输出文件仅包含一个整数,表示一共可能的称量方式对10^9的模。
Sample Input
Sample Output
样例解释
一共有三种方式称量出166。166=64+64+16+16+4+1+1。166=256-64-16-16+4+1+1。166=256-64-16-4-4-1-1。
HINT
Source
分析
讲真,这题动态规划的思路不难,上了趟WC就有了,但是废了好大劲才把这个巨型整数变成4进制,当然中间是“天马行空”就是了。
假如已经有了这个整数的四进制表示方法,如166的四进制数2212,称第1个2为第1位,第2个2为第2位,而1为第3位。
动态规划如下:
F[i][0]表示第i位上目前就是第i位数字的最小操作数。
F[i][1]表示第i位上目前是第i位数字+1的最小操作数。
G[i][0]和G[i][1]分别对应两者的方案数。
对于F数组,有如下转移——
F[i][0] <- F[i - 1][0] + num[i]
F[i][0] <- F[i - 1][1] + 4 - num[i]
F[i][1] <- F[i - 1][0] + num[i] - 1
F[i][1] <- F[i - 1][1] + 3 - num[i]
显然就是枚举达到当前转态要求的数字,可以用什么方式得到。要么是从0加到num[i],要么是从4减到num[i]。
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm> using namespace std; #define lim 100000 int stk[lim]; class BigNum
{
private:
int s[lim]; char gc(void)
{
return getchar();
} void pc(char c = '\n')
{
putchar(c);
} int gl(void)
{
int len = lim; while (!s[--len] && len); len = len == ? : len; return len;
} public:
BigNum(void)
{
memset(s, , sizeof(s));
} void read(void)
{
int tot = , len = ; for (char c = gc(); c >= ''; c = gc())
stk[++tot] = c - ''; while (tot)s[++len] = stk[tot--];
} void print(void)
{
for (int len = gl(); len; )
pc(s[len--] + '');
} void println(void)
{
print(); pc();
} int mod4(void)
{
int res = ; for (int len = gl(); len; )
res = (res* + s[len--]) & ; return res;
} void div4(void)
{
for (int len = gl(); len; --len)
s[len - ] += (s[len] & )*, s[len] >>= ; s[] = ;
} bool not0(void)
{
if (gl() > || s[])
return true;
return false;
}
}num; const int MOD = 1e9; int f[lim][];
int g[lim][]; void Min(int &a, int b)
{
a = min(a, b);
} void add(int &a, int b)
{
a += b; if (a >= MOD)
a -= MOD;
} signed main(void)
{
num.read(); int tot = ; while (num.not0())
stk[++tot] = num.mod4(), num.div4(); reverse(stk + , stk + + tot); memset(g, , sizeof(g));
memset(f, 0x3f3f3f3f, sizeof(f)); f[][] = ; g[][] = ;
f[][] = ; g[][] = ; for (int i = ; i <= tot; ++i)
{
Min(f[i][], f[i - ][] + stk[i]);
Min(f[i][], f[i - ][] + - stk[i]);
Min(f[i][], f[i - ][] + stk[i] + );
Min(f[i][], f[i - ][] + - stk[i]);
} for (int i = ; i <= tot; ++i)
{
if (f[i][] == f[i - ][] + stk[i])
add(g[i][], g[i - ][]);
if (f[i][] == f[i - ][] + - stk[i])
add(g[i][], g[i - ][]);
if (f[i][] == f[i - ][] + stk[i] + )
add(g[i][], g[i - ][]);
if (f[i][] == f[i - ][] + - stk[i])
add(g[i][], g[i - ][]);
} printf("%d\n", g[tot][]);
}
BZOJ_1111.cpp
@Author: YouSiki