HDU_Steps7.3 母函数,组合 HDU2451 HDU1398 HDU2152 HDU1709 HDU1028 HDU3398 HDU2179

HDU_Steps7.3母函数,组合

 

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);
	}
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值