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