09-01 HDU_Steps2.2 HDU1568 HDU1443 HDU1997 HDU1222 HDU 1249 HDU1013 HDU1066 HDU2674

HDU_Steps2.2题解

感觉这个Section里的题目还是有些难度的..可能是因为我比较菜吧..


2.2.1 HDU1568 Fibonacci 

求肥不拉几数列前四位,记得集训的时候做过一题求前四位和后四位的,当时没有做出来,后来也没怎么看,正好这一题就自己写了一下

10^8的数量级,大数也是放不下,而且必然TLE,肯定是用公式了,这个可以看一下维基百科里对肥不拉几数列的说明,log后  log(an)=log(sq5/5)+n*log((1+sq5)/2)+log(1-(1-sq5)/(1+sq5) ^n)  默认底为10,其中sq5=sqrt(5)

为什么要这样做呢?观察这个式子,log(1.0324*10^n)=n+log(1.0324),所以t-(floor)t=log(1.0324)
1032就是前4位,所以pow(10,log(1.0324)+3)=1032.4 得出前4位 

其实像那些求数有多少位的题目都可以用log(N)/log(10)来得出答案

#include <cstdio>
#include <cstdlib>
#include <math.h>
using namespace std;
int main(){
	//init fib[i]<10000
	int fib[100],fibs;
	fib[0]=0,fib[1]=1;
	for(fibs=2;fib[fibs-1]<10000;fibs++)fib[fibs]=fib[fibs-1]+fib[fibs-2];
	fibs-=2;
	/*
		<10000直接得出答案
		an=sq5/5*((1+sq5)/2 ^n-(1-sq5)/2 ^n) 
		log(an)=log(sq5/5)+n*log((1+sq5)/2)+log(1-(1-sq5)/(1+sq5) ^n)  默认log10
		log(1.0324*10^n)=n+log(1.0324),所以t-(floor)t=log(1.0324)
		1032就是前4位,所以pow(10,log(1.0324)+3)=1032.4 得出前4位 
	*/
	int n;
	double sq5=sqrt(5);
	while(scanf("%d",&n)!=EOF){
		if(n<=fibs)printf("%d\n",fib[n]);
		else{
			double tmp=(log(sq5/5)+n*log(0.5+sq5/2))/log(10.0);
			tmp=tmp-floor(tmp);	
		 	printf("%d\n",(int)pow(10,tmp+3));
		}
	} 
	return 0;	
} 


2.2.2 HDU 1443 Joseph

前k个好人,后k个坏人,坏人出完之前好人不能出列,我是用双向循环链表直接模拟的 其中双向循环链表是用数组模拟的,跑了400MS+,如果觉得慢直接打个表提交就可以了,反正就1~13嘛..

#include <cstdio>
#include <string.h>
#include <cstdlib>
using namespace std;
int solve(int k,int m){
	int gs=0,bs=0,st=1;//好人数,坏人数,起点 
	//数组模拟双向链表
	int next[30];
	int pre[30];
	for(int i=1;i<=2*k;i++)next[i]=i+1;
	for(int i=2;i<=2*k;i++)pre[i]=i-1;
	next[2*k]=1,pre[1]=2*k;
	
	for(int i=k*2;i>=1;i--){
		//模去当前长度,比如长度为4时,报2和报6是一样的 
		int tk=m%i; 
		if(tk==0)tk=i;
		//报数 
		for(int j=1;j<tk;j++){
			st=next[st];
		}
		
		if(st<=k)gs++;else bs++;
		if(gs==1)return 0;
		if(bs==k)return 1;//坏人全部退出了 
		
		//删除这个点 
		int tmp=st;
		st=next[st];
		next[pre[tmp]]=st;
		pre[st]=pre[tmp];
		
	}	
}
int main(){
	int k;
	int s[14];
	for(int i=1;i<=13;i++){
		int j=i;
		while(++j){
			if(solve(i,j))break;	
		}	
		s[i]=j;
	}
	while(scanf("%d",&k),k){
		printf("%d\n",s[k]);	
	}
} 


2.2.3 HDU1997 汉诺塔

关键是对汉诺塔那个递归程序的理解,递归这个理解了,这题就不难了

比如N=3,先考虑最大的3,最后一步要将3从A->C, 所以3只能在A上或者C上,在B上则错误

若3在C上,说明3已达目的地,此时的目的就是将2移动到3上.而此时2若不在C上.则必在B的最下.在A上则错误.(交换AB)

若3在A上,说明3未动,此时的目的是将2移动到B上,若2若不再A上,则目的是将A移到B,在C上则错误(交换BC)

#include <cstdio>
#include <string>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n,ns,n2,a[65],yes;
int main(){
	int cas;
	scanf("%d",&cas);
	while(cas--){ 
		scanf("%d",&n);
		for(int i=1;i<=3;i++){
			scanf("%d",&ns);
			//标记每个块在哪跟木棍上 
			for(int j=1;j<=ns;j++){
				scanf("%d",&n2);
				a[n2]=i;
			}	
		}
		
		/*
			递归的思想,比如1 2 3
			最后一步是要将3从A移动到C上,不可能在B上,所

以sor->A,cant->B,des->C
			考虑3现在的位置,如果3在sor上,即未动,则任务

变为将2移动到B上,交换des和cant
			如果3在des上,即已到达目标位置,则任务变为将2

移动到C上,浇花sor和cant 
		*/ 
		yes=1;
		int sor=1,cant=2,des=3,t;//源点柱,不能在的柱子,目的

柱
		for(int i=n;i>=1;i--){
			if(a[i]==cant){yes=0;break;}
			else if(a[i]==des)t=sor,sor=cant,cant=t;
			else if(a[i]==sor)t=des,des=cant,cant=t; 
		}
		
		if(yes)printf("true\n");
		else printf("false\n");	
	}
    //system("pause");
    return 0;
}


2.2.4 HDU1222 Wolf and Rabbit 直接用结论,两数互质的话则狼可以到达所有洞,判断GCD(A,B)==1


2.2.5 HDU1249 三角形 其实这种平面分割的公式大多是二次的,递推也可,最简单的是直接找三个点解三元一次方程组(不能保证适用所有情况...),公式是F(n)=3*n*n-3*n+2


2.2.6 HDU1013 Digital Roots 注意一下大数的问题就可以了,当然,虽然输入是大数,但完全不必用大数运算,因为输入的数字各位数和是在32范围之内的~~

#include <cstdio>
#include <string.h>
using namespace std;
int main(){
	char line[1000];
	int n;
	//注意输入用数组 之后用int就可以了 
	while(scanf("%s",line),line[0]!='0'){
		n=0;
		for(int i=0;i<strlen(line);i++)n+=line[i]-'0'; 
		while(n>9){
			int r=0;
			while(n>0){
				r+=n%10;
				n/=10;
			}
			n=r;
		}	
		printf("%d\n",n);
	}
	return 0;	
}

2.2.7  HDU1066 Last non-zero Digit in N!

以前做过求N!最后范围非0数的题目的,以为这题可以水过..没想到却卡了,因为这题的数据实在太大,只能另辟蹊径了..在网上看了各牛的文章,看了很久终于弄明白了..还是贴一下各牛们的解答吧

http://www.cnblogs.com/superbin/archive/2010/09/28/1837671.html 该牛的文章比较简洁

http://blog.csdn.net/fengyu0556/article/details/5615129 这里有比较详细的解释

N! = 5 * (floor(N/5)!) * An, f(N!) = f(floor(N/5)!)*f(5*An)  显然,对N/5!递归处理,现在的问题就是求f(5*An),考验人对数字的敏感程度啊..这里我是用java写的大数..

import java.math.BigInteger;
import java.util.Scanner;


/*
	N! = 5 * (floor(N/5)!) * An, f(N!) = f(floor(N/5)!)*f(5*An);递归处理
*/
public class Main {
	public static void main(String[] args) {
		new Main();
	}
	public Main(){
		Scanner sc=new Scanner(System.in);
		BigInteger n,fives;
		int mod[]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2,1};
		while(sc.hasNext()){
			n=sc.nextBigInteger();
			int res=1;
			if(n.subtract(g(1)).signum()!=1)res=mod[20];
			else while(n.signum()==1){
				res=res*mod[Integer.parseInt(n.mod(g(20)).toString())]%10;
				n=n.divide(g(5));
			}
			System.out.println(res);		
			
		}
	}
	
	public BigInteger g(int x){
		return new BigInteger(new Integer(x).toString());
	}
}

2.2.8 HDU2674 N! Again

这题比较简单了,注意答案是MOD2009 而2009=7*7*41 所以41! 之后MOD%2009就都是0了~~



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值