【BZOJ1111】[POI2007] 四进制的天平Wag(DP)

点此看题面

大致题意: 要把一个高精度大整数表示为若干(4)的幂的和与差,使得所用(4)的幂数量最少。求方案数。

前言

似乎没什么可说的。。。

大力找规律找半天,最后发现是道(DP)题,求心理阴影面积。。。

马上就要吃饭了,也来不及扯啥了,赶紧水完走人。

动态规划

首先,遇上这种题,肯定先把十进制转化为四进制。

考虑我们定义一个结构体(S),由(f,g)两个变量组成,分别表示数量方案数

定义两个(S)变量(A,B)的加法为:

  • (A.f=B.f),则返回(S(A.f,A.g+B.g))
  • 否则,返回(f)较小的那一个。

显然,如果要使某一位满足条件,无非是两种方式:加法或减法。

因此,我们设(F_x)(G_x)分别表示从高到低(DP)到第(x)位,使高于(x)位的都满足条件,第(x)位满足条件/比目标值大(1)(用于退位)的方案数。

则有转移方程:

[F_x=S(F_{x+1}.f+v_x,F_{x+1}.g)+S(G_{x+1}.f+4-v_x,G_{x+1}.g) ]

[G_x=S(F_{x+1}.f+v_x+1,F_{x+1}.g)+S(G_{x+1}.f+3-v_x,G_{x+1}.g) ]

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define LEN 2000
#define X 1000000000
using namespace std;
int t,v[LEN+5];
struct S
{
	int f,g;I S(CI x=0,CI y=0):f(x),g(y){}
	I friend S operator + (Con S& A,Con S& B) {return A.f^B.f?(A.f<B.f?A:B):S(A.f,(A.g+B.g)%X);}
}F[LEN+5],G[LEN+5];
struct DecSystem
{
	int l,a[LEN+5];
	I void read()//读入
	{
		static string s;cin>>s,l=s.length();for(RI i=1;i<=l;++i) a[i]=s[l-i]&15;
	}
	I int Mod4()//除以4,返回余数(用于进制转化)
	{
		RI i,w=0;for(i=l;i;--i) a[i]=10*w+a[i],w=a[i]%4,a[i]/=4;W(l&&!a[l]) --l;return w;
	}
}n;
int main()
{
	RI i;n.read();W(n.l) v[++t]=n.Mod4();//十进制转四进制
	for(F[t+1]=S(0,1),G[t+1]=S(1,1),i=t;i;--i)//动态规划
		F[i]=S(F[i+1].f+v[i],F[i+1].g)+S(G[i+1].f+4-v[i],G[i+1].g),//转移F
		G[i]=S(F[i+1].f+v[i]+1,F[i+1].g)+S(G[i+1].f+3-v[i],G[i+1].g);//转移G
	return printf("%d",F[1].g),0;//输出方案数
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1111.html