7.3.1 HDU2451 Simple Addition Expression 组合
求三个数相加不进位的情况有多少种,显然个位数是0,1,2,最高位是1,2,3,其它位是0,1,2,3。
#include<cstdio>
#include<cmath>
#include<string.h>
using namespace std;
char s[15];
//传入数组下标和当前位置开始剩余的长度
int solve(int i,int p){
if(p==1){
return s[i]>'2'?3:s[i]-'0'+1;
}
//当前值大于3,除了个位都可以取0-3,个位取0,1,2
if(s[i]>'3'){
return (int)pow(4.0,p-1)*3;
}else{
//s[i]=3时,取0,1,2,s[i]=2时,取0,1,s[i]=1时,取0,后面的数字可以取0-3,个位取0-2
int t1=(int)(pow(4.0,p-2)*3*(s[i]-'0'));
int t2=solve(i+1,p-1);//处理s[i]=3(或者2,或者1,看前一位)的情况
return t1+t2;
}
}
int main(){
__int64 n;
while(scanf("%I64d",&n)!=EOF){
n--;
sprintf(s,"%I64d",n);
printf("%d\n",solve(0,strlen(s)));
}
return 0;
}
7.3.3 HDU1398 Square Coins
母函数模板题
#include <cstdio>
#include <string.h>
using namespace std;
int c1[305],c2[305],n;
int main(){
while(scanf("%d",&n),n){
for(int i=0;i<=n;i++){
c1[i]=1;//全部由1组成
c2[i]=0;
}
for(int i=2;i*i<=n;i++){
for(int j=0;j<=n;j++){
for(int k=0;k+j<=n;k+=i*i){
c2[j+k]+=c1[j];
}
}
memcpy(c1,c2,sizeof c2);
memset(c2,0,sizeof c2);
}
printf("%d\n",c1[n]);
}
}
7.3.4 HDU2152 Fruit
上下限母函数
#include <cstdio>
#include <string.h>
using namespace std;
int n,m,ar[105][2],c1[105],c2[105];
//上下限母函数
//f(x)=(x^ar[1][0]+...+x^ar[1][1])(...)(x^ar[n][0]+x^ar[n][1])
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d%d",&ar[i][0],&ar[i][1]);
}
memset(c2,0,sizeof c2);
memset(c1,0,sizeof c1);
for(int i=ar[1][0];i<=ar[1][1];i++)c1[i]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=ar[i][0];k<=ar[i][1];k++){
c2[k+j]+=c1[j];
}
}
memcpy(c1,c2,sizeof c2);
memset(c2,0,sizeof c2);
}
printf("%d\n",c1[m]);
}
return 0;
}
7.3.5 HDU1709 The Balance
问给定砝码不能称出的重量有多少种,DP,为了防止这一次的状态对这次DP造成影响,用两个数组来进行操作,d[]表示上一次操作的结果,d2[]为这次操作的结果,注意对负数的处理
#include <cstdio>
#include <string.h>
using namespace std;
int sum,n,an[105],d[11005],d2[11005],rss[10005];;
int main(){
while(scanf("%d",&n)!=EOF){
sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&an[i]);
sum+=an[i];
}
//DP
memset(d,0,sizeof d);
memset(d2,0,sizeof d2);
d[0]=1;
d2[0]=1;
for(int i=1;i<=n;i++){
for(int j=an[i];j<=sum;j++){
if(d[j])d2[j-an[i]]=1;
}
//负数
for(int j=0;j<=an[i];j++){
if(d[j])d2[an[i]-j]=1;
}
for(int j=sum-an[i];j>=0;j--){
if(d[j])d2[j+an[i]]=1;
}
for(int j=0;j<=sum;j++)d[j]=d2[j];
}
//统计
int rs=0;
for(int i=1;i<=sum;i++)
if(d[i]==0){
rs++;
rss[rs]=i;
}
printf("%d\n",rs);
if(rs==0)continue;
for(int i=1;i<=rs;i++){
printf("%d",rss[i]);
if(i!=rs)printf(" ");
}
printf("\n");
}
}
7.3.6 HDU1028 Ignatius and the Princess III
母函数自然可以做,而且是最裸的了,我这里是用递推做的,算法课本上的原题,不过要注意记忆化
d[n][m]表示最大加数为m构造n有多少种方法,具体递推在注释中
#include <cstdio>
#include <string.h>
using namespace std;
int d[125][125];//注意要记忆化,不记忆会TLE
int cal(int n,int m){
if(d[n][m]!=0)return d[n][m];
if(n==1||m==1)d[n][m]=1;//最大加数为1
else if(n==m)d[n][m]=1+cal(n,n-1);//最大加数为n,有n=n和最大加数为n-1的情况组成
else if(n>m)d[n][m]=cal(n-m,m)+cal(n,m-1);最大加数为m,则由最大加数为m组成n-m,或者最大加数为m-1组成n
else d[n][m]=cal(n,n);//最大加数必须小于这个数
return d[n][m];
}
int main(){
int n;
memset(d,0,sizeof d);
while(scanf("%d",&n)!=EOF){
printf("%d\n",cal(n,n));
}
return 0;
}
7.3.7 HDU3398 String
求由n个1、m个0组成,并且任意前缀中1的个数不少于0的个数的字符串的个数,并模20100501
看了报告才会,赞啊,很巧妙的思路,转换成坐标系。
初始在坐标系的原点(0,0),从字符串第一位开始,碰到一个1就向上走,碰到一个0就向右走,走到(n,m)显然为c(n+m,n)为总的情况数
对于任意前缀中1的个数不少于0的个数的字符串的个数这个条件,可以看成是坐标系中,从(0,0)点走到(m, n)点,并且跟y=x-1这条直线不相交的方案数。又因为(0,0)点关于直线y=x-1的对称点是(1,-1),而从(1,-1)点走到(m, n)点的所有方案一定都会与直线y=x-1相交,对于这些方案,将从(1,-1)点到与y=x-1的第一个交点之间的路径关于y=x-1对称翻转过去,就可以得到所有不满足题意的从(0,0)点走到(m, n)点的方案,于是最终答案就是C(n+m, n)-C(n+m,n+1)。
推出F(M,N)=(N+1-M)*(N+M)!/((N+1)!*M!)接下来就是约分了,涉及质因数分解
#include <cstdio>
#include <string.h>
#define P 20100501
#define MAXN 1000001
using namespace std;
typedef long long LL;
//ans=(n+1-m)(n+m)!/(n+1)!m!
int pri[MAXN*2+1],p[MAXN*2+1],tot;//素数表,及素数个数
int cal(int pr,int n){
int rs=0;
while(n>0){
n/=pr;
rs+=n;
}
return rs;
}
void initp(){
memset(pri,0,sizeof pri);
tot=0;
for(int i=2;i<=MAXN*2;i++){
if(pri[i])continue;
p[tot++]=i;//记录素数
for(int j=i*2;j<=MAXN*2;j+=i){
pri[j]=1;
}
}
}
int main(){
int cas,n,m;
initp();
scanf("%d",&cas);
while(cas--){
scanf("%d%d",&n,&m);
LL rs=1;
int nm=n-m+1;//注意先对n-m+1分解
//对n!分解质因数,一边分解一边计算
for(int i=0;i<tot&&p[i]<=n+m;i++){
int cnt=0;
while(nm%p[i]==0){
nm/=p[i];
cnt++;
}
int ipow=cnt+cal(p[i],n+m)-cal(p[i],n+1)-cal(p[i],m);
for(int j=1;j<=ipow;j++){
rs=(rs*p[i])%P;
}
}
printf("%lld\n",rs);
}
return 0;
}
7.3.8 HDU2179 pi
这题做了很久很久!自己用幂级数展开的知识做的,但是精度不够,看了很多文章,最后终于找到了一个给力的函数,配合java的大数,a之。。
公式是 PI=2+(1/3×(2+2/5×(2+...)))大概要迭代到5000层左右才能精确到1500位
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
BigDecimal TWO=BigDecimal.valueOf(2);
BigDecimal ans=BigDecimal.valueOf(2);
for(int i=5000;i>=1;i--){
ans=TWO.add(ans.multiply(td(i)).divide(td(2*i+1),1800,BigDecimal.ROUND_HALF_UP));
}
String stra=ans.toString();
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
int n=sc.nextInt();
if(n==0)break;
System.out.println("3.");
for(int i=0;i<n;i++){
System.out.print(" ");
System.out.print(stra.substring(i*5+2,i*5+7));
if((i+1)%10==0)System.out.println();
}
if(n%10!=0)System.out.println();
}
}
public static BigDecimal td(int n){
return BigDecimal.valueOf(n);
}
}