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]);
}
}
关键是对汉诺塔那个递归程序的理解,递归这个理解了,这题就不难了
比如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;
}
以前做过求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());
}
}
这题比较简单了,注意答案是MOD2009 而2009=7*7*41 所以41! 之后MOD%2009就都是0了~~