随笔 - 95  文章 - 17 评论 - 13 trackbacks - 0

目录

蟠桃记 1

折线分割平面 2

不容易系列之一 2

骨牌铺方格 3

不容易系列之(3)—— LELE的RPG难题 3

Children’s Queue 3

献给杭电五十周年校庆的礼物 3

钥匙计数之二 3

钥匙计数之一 3

母牛的故事 3

超级楼梯 3

不容易系列之二 3

一只小蜜蜂... 3

阿牛的EOF牛肉串 3

神、上帝以及老天爷 3

不容易系列之(4)——考新郎 3

 

 

蟠桃记

http://acm.hdu.edu.cn/showproblem.php?pid=2013

#include<cstdio>

int f(int n)

{

if(n==1)

return 1;

return f(n-1)*2+2;

}

int main()

{

int n;

while(scanf("%d",&n)!=EOF)

{

printf("%d\n",f(n));

}

return 0;

}

简单题

折线分割平面

http://acm.hdu.edu.cn/showproblem.php?pid=2050

#include<cstdio>

int main()

{

int t;

int n;

scanf("%d",&t);

while(t--)

{scanf("%d",&n);

printf("%d\n",2*n*n-n+1);

}

return 0;

}

找到n=1 是f=2,n=2,f=7,n=3,f=16,根据这三个量,求出二次方程。

 

 

 

 

 

不容易系列之一

http://acm.hdu.edu.cn/showproblem.php?pid=1465

#include<cstdio>

int main()

{

int n;

while(scanf("%d",&n)!=EOF)

{

long long pre=0,f=0,t=-1;

for(int i=1;i<n;++i)

{

t*=-1;

f=pre*(i+1)+t;

pre=f;

}

printf("%I64d\n",f);

}

return 0;

}

错位排列数Dn=nD(n-1)+(-1)^n

 

骨牌铺方格

http://acm.hdu.edu.cn/showproblem.php?pid=2046

#include<cstdio>

int main()

{

int n;

long long f[51];

f[1]=1;f[2]=2;

for(int i=3;i<=50;++i)

{

f[i]=f[i-1]+f[i-2];

}

while(scanf("%d",&n)!=EOF)

{

printf("%I64d\n",f[n]);

}

return 0;

}

F[n]=f[n-1]+f[n-2]

N-1是添加的牌放竖的情况,n-2是牌放横的情况。

 

不容易系列之(3)—— LELE的RPG难题

http://acm.hdu.edu.cn/showproblem.php?pid=2045

#include<cstdio>

int main()

{

long long f[51];

f[1]=3;f[2]=6;f[3]=6;

for(int i=4;i<=50;++i)

f[i]=f[i-1]+f[i-2]*2;

int n;

while(scanf("%d",&n)!=EOF)

{

printf("%I64d\n",f[n]);

}

return 0;

}

先初始n=1,2,3的情况

然后我们假设长度为n的序列,倒数第二个与第一个相同,则此时的情况有f[n-2]*2钟可能,因为倒数第二与第一相同,故最后一个有两种情况。设倒数第二个与第一个不同,则此时情况为f[n-1]*1,因为此时最后一个只能取一种可能

 

Children’s Queue

http://acm.hdu.edu.cn/showproblem.php?pid=1297

#include<cstdio>

void add(int a[],int b[])

{

for(int i=0;i<100;++i)

{

a[i]+=b[i];

if(a[i]>9)

{

++a[i+1];

a[i]-=10;

}

}

}

int main()

{

int n;

int f[1001][100]={0};

f[0][0]=1;

f[1][0]=1;

f[2][0]=2;

f[3][0]=4;

for(int i=4;i<1001;++i)

{

add(f[i],f[i-1]);

add(f[i],f[i-2]);

add(f[i],f[i-4]);

}

while(scanf("%d",&n))

{

int i=99;

while(f[n][i]==0) --i;

while(i>=0) printf("%d",f[n][i--]);

printf("\n");

}

return 0;

}

我们假设一个合法序列,我们加上FF, M,MFFF都可以得到一个合法序列

所以一个合法序列f[n]=f[n-1]+f[n-2]+f[n-4]

另外这是一道高精度题。

 

献给杭电五十周年校庆的礼物

http://acm.hdu.edu.cn/showproblem.php?pid=1290

#include<cstdio>

int main()

{

long long n;

while(scanf("%I64d",&n)!=EOF)

{

printf("%I64d\n",(n*n*n+5*n)/6+1);

}

return 0;

}

这里转一下网上的一篇讲解

(1) n条直线最多分平面问题

       题目大致如:n条直线,最多可以把平面分为多少个区域。

       析:可能你以前就见过这题目,这充其量是一道初中的思考题。但一个类型的题目还是从简单的入手,才容易发现规律。当有n-1条直线时,平面最多被分成了f(n-1)个区域。则第n条直线要是切成的区域数最多,就必须与每条直线相交且不能有同一交点。 这样就会得到n-1个交点。这些交点将第n条直线分为2条射线和n-2条线断。而每条射线和线断将以有的区域一分为二。这样就多出了2+(n-2)个区域。

          故:f(n)=f(n-1)+n

                       =f(n-2)+(n-1)+n

                       ……

                       =f(1)+1+2+……+n

                       =n(n+1)/2+1


         (2) 折线分平面(hdu2050)

        根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当n-1条折线时,区域数为f(n-1)。为了使增加的区域最多,则折线的两边的线段要和n-1条折线的边,即2*(n-1)条线段相交。那么新增的线段数为4*(n-1),射线数为2。但要注意的是,折线本身相邻的两线段只能增加一个区域。

       

        故:f(n)=f(n-1)+4(n-1)+2-1

                       =f(n-1)+4(n-1)+1

                      =f(n-2)+4(n-2)+4(n-1)+2

                      ……

                      =f(1)+4+4*2+……+4(n-1)+(n-1)   

                      =2n^2-n+1

       (3) 封闭曲线分平面问题

       题目大致如设有n条封闭曲线画在平面上,而任何两条封闭曲线恰好相交于两点,且任何三条封闭曲线不相交于同一点,问这些封闭曲线把平面分割成的区域个数。

        析:当n-1个圆时,区域数为f(n-1).那么第n个圆就必须与前n-1个圆相交,则第n个圆被分为2(n-1)段线段,增加了2(n-1)个区域。

  

              故: f(n)=f(n-1)+2(n-1)     

                              =f(1)+2+4+……+2(n-1)

                              =n^2-n+2

           (4)平面分割空间问题(hdu1290)

           由二维的分割问题可知,平面分割与线之间的交点有关,即交点决定射线和线段的条数,从而决定新增的区域数。试想在三维中则是否与平面的交线有关呢?当有n-1个平面时,分割的空间数为f(n-1)。要有最多的空间数,则第n个平面需与前n-1个平面相交,且不能有共同的交线。即最多有n-1 条交线。而这n-1条交线把第n个平面最多分割成g(n-1)个区域。(g(n)为(1)中的直线分平面的个数 )此平面将原有的空间一分为二,则最多增加g(n-1)个空间。

         

         故:f=f(n-1)+g(n-1)     ps:g(n)=n(n+1)/2+1

                    =f(n-2)+g(n-2)+g(n-1)

                    ……

                   =f(1)+g(1)+g(2)+……+g(n-1)

                  =2+(1*2+2*3+3*4+……+(n-1)n)/2+(n-1)

                  =(1+2^2+3^2+4^2+……+n^2-1-2-3-……-n )/2+n+1

                 =(n^3+5n)/6+1

钥匙计数之二

http://acm.hdu.edu.cn/showproblem.php?pid=1480

#include<cstdio>

int main()

{

int n;

long long t16=16,t2345=18,ans=104,a_2=4;

printf("N=%d: %I64d\n",3,ans);

for(int i=4;i<=25;++i)

{

t16=ans-t16+4*(1*a_2-1)+6*(a_2*2-2);

t2345=ans+(a_2*2-2)*9;

a_2*=2;

ans=t16*2+t2345*4;

printf("N=%d: %I64d\n",i,ans);

}

return 0;

}

转一份解题报告:

二、题目分析

提交完本题后,颇觉有递推的味道。

出发点:构造结果ans[n]=num[1]+……+num[6];其中num[i]表示以i为最后一个槽的高度;计算出num[i],从而得出结果。

 

首先进行初始化分析,即n=3时。

“相连的槽其深度之差不得为5”——1,6这两个高度不能相邻;而2,3,4,5这四个高度等价,且之后n=4,5,……25的计算过程中均有此规律。即num[1]=num[6],num[2]=num[3]=num[4]=num[5],在写代码时注意到这点便可以不需要用到数组;

num[1]=num[6]=16;num[2,3,4,5]=18;

ans[3]=104;

 

(下面是重点)

再由n-1递推分析n的情况:

1、 当前面n-1个排列是钥匙的排列,则

A、 对2,3,4,5作为第n个高度来说都能满足题意,有num[2,3,4,5]=ans[n-1];

B、 对1,6(1,6等价,记号不同而已)来说,第n-1个高度不能为6,1,即要去掉

几个不符合题意的组合;num[1]=ans[n-1]-num__[6](前n-1个中最后一个为6的个数,实际写代码时要用另一个数组保存)。同理 num[6]=ans[n-1]-num__[1](……)。也即num[1,6]=ans[n-1]-num__[6](……);

 

2、 当前面n-1个排列不是钥匙的排列,则

A、对i(i=2,3,4,5)作为第n个高度来说能满足钥匙的要求,则说明前面n-1个排列里仅有两类高度,且与i不同,加上i就刚好3类高度满足题意。那么前面两类高度的选法总数是从其余5类高度里选出两类,即C(5,2),但1,6不能同时选,故组合数为

C(5,2)-1。 再看排列数,n-1个位置,每个位置可任选两类,但不能全部是同一类高度,故排列数2^(n-1)-2。

B、对i=1,6,同上面分析。因为1,6等价,所以我这里举i=1来说,前面两类高度里我有两种取法,选6和不选6。

对于选6,组合数是C(4,1)(剩下2,3,4,5任意选一);再看排列数,每个位置可任选两类,但不能全部是同一高度,且最后一个也即第n-1个位置处不能为6,也可换个说法,最后一个位置放i(i=2,3,4,5),前面n-2个位置任选6和i放,排列数4×2^(n-2)。

对于不选6,组合数是C(4,2);再看排列数,每个位置可任选两类,且不能全部是同一类高度,排列数2^(n-1)-2。

 

把上面的组合数与排列数相乘便得到一种情况下的num[i]的值,所有情况的值相加便得到结果。

 

三、代码(从简)

/*Accepted 1480 0MS 192K 743B G++ */

    

    t[6]=16;ans[3]=104;

    for(i=4;i<26;i++)

    {

        //前n-1是钥匙的情况;

        num[1]=ans[i-1]-t[6];

        num[2]=ans[i-1]; 

        

//前n-1不是钥匙的情况;

        num[1]+=c42*(mult(2,i-1)-2);   //不含有6;mult()算阶乘;

          

        num[1]+=4*(mult(2,i-2)-1);   //含有6;

          

        num[2]+=(c52-1)*(mult(2,i-1)-2);

          

        ans[i]=2*num[1]+4*num[2];

        t[6]=num[1];

    }

   

/*以上代码可以不写mult函数,在数据量大的情况下能省很多时间,请读者自己思考,

以上代码可以不写数组,在数据量大的情况下能省内存占用。希望读者完善之*/

四、总结(略)

刚开始接触这题,有点恐惧,尽管自己手动分析了n=3,4的情况,感觉情况似乎很多种,不敢往下接着想,和以前做这类题一样,以为会有什么很高深的数学公式可以用,就没有继续下去。

AC完后就会有似曾相识的感觉,类似于http://acm.hdu.edu.cn/showproblem.php?pid=3284

这类由n-1递推到n的题,其实还有好几道,只是一时没找不到,发现这类题的共同点就是选定某个位置作为结果的一部分,用数组保存,n-1到n之间由这个数组来递推。本题hdu1480网上还有一个十分详细的解题报告(http://www.programfan.com/blog/article.asp?id=45140),本人未看,不知是否大体相同,若看不懂本解题报告,可参考之。

 

   hdu 1480  钥匙计数之二  

 

 一把钥匙有N个槽,2<N<26槽深为1,2,3,4,5,6。每钥匙至少有3个不同

 的深度且相连的槽其深度之差不得为5。求这样的钥匙的总数。

 

解题分析

 

key[n]是有n个槽的钥匙的总数,

num[i]是最后一个槽的高度为i的钥匙总数 

n=3时

   abc 是 钥匙且c=1,

   则 b!=6,b=2,3,4,5有4种,a是除c,b 外的4种,因此有

   3个槽且最后一个槽高为1的 钥匙有 16种,记为 num[1]=16; 

 

  如果 abc是 钥匙, 则 (7-a)(7-b)(7-c) 也是

  例如  1234是 钥匙 则 6543 也是

  这说明 num[i]=num[7-i],num[1]=num[6];

  

   abc 是 钥匙且c=2,

   则 a,b 是 1,3,4,5,6中挑2个但不挑(1,6),有c(5,2)-1=9种,因此有

   3个槽且最后一个槽高为2的 钥匙有 9*2=18种, num[2]=18; 

  易知 num[2]=num[3]=num[4]=num[5]; 

n=3, key[3]=num[1]+...+num[6]=104;

 

 

设num[i]和temp[i]分别是n-1和n个槽的最后槽高为i的钥匙总数

 

1、 设 **** 是n-1个槽的钥匙,则 ****d是 n个槽的钥匙,d=2,3,4,5.

    即temp[d]=key[n-1]+s;

    设 **** 不是n-1个槽的钥匙, ****d是 n个槽的钥匙,d=2,3,4,5.

    则 ****是除d外的两个数字构成且这两个数字不能是(1,6) ,有

    s=(c(5,2)-1)*(2^(n-1)-2)=9*(2^(n-1)-2)种

    即

    temp[d]=key[n-1]+ 9*(2^(n-1)-2);

    

2  设 **** 是n-1个槽的钥匙且最后槽高不为6,则 ****1是 n个槽的钥匙。

    即

    temp[1]=key[n-1]-num[6]+s;

    设 **** 不是n-1个槽的钥匙, ****1是 n个槽的钥匙

    则 **** 由2,3,4,5中的两个构成 

    有c(4,2)*(2^(n-1)-2)种,再加

    ****由2,3,4,5中的1个和6构成6不能是最后一个

    有4*(2^(n-2)-1)种

   即

   temp[1]=key[n-1]-num[6]+ 6*(2^(n-1)-2)+ 4*(2^(n-2)-1);

   

         = key[n-1]-num[6]+ 16*(2^(n-2)-1)  

   temp[6]=temp[1];

   

   for(n=4;n<=25;n++)

   {

                     

    key[n]=................. 

                     

   }

 

钥匙计数之一

http://acm.hdu.edu.cn/showproblem.php?pid=1438

#include<cstdio>

int main()

{

int n;

long long t14=4,t23=4,ans_1=16,ans_2=24,a_2=4;

printf("N=2: 0\n");

printf("N=3: %I64d\n",ans_2-ans_1);

for(int i=4;i<=31;++i)

{

t14=ans_1-t14+(a_2*2-2)+2*(a_2-1);

t23=ans_1+2*(a_2*2-2);

ans_1=t14*2+t23*2;

ans_2=(ans_2+3*(a_2*2-2))*4;a_2*=2;

printf("N=%d: %I64d\n",i,ans_2-ans_1);

}

return 0;

}

由上一道题改编而成,思想差不多。

 

母牛的故事

http://acm.hdu.edu.cn/showproblem.php?pid=2018

#include<cstdio>

int main()

{

int n;

int f[56];

f[1]=1;f[2]=2;f[3]=3;

for(int i=4;i<54;++i)

f[i]=f[i-3]+f[i-1];

while(scanf("%d",&n),n)

{

printf("%d\n",f[n]);

}

return 0;

}

列出前几项,可推出fibonacci序列。

 

超级楼梯

http://acm.hdu.edu.cn/showproblem.php?pid=2041

#include<cstdio>

int main()

{

int f[41];

f[1]=1;f[2]=1;

for(int i=3;i<=40;++i)

f[i]=f[i-1]+f[i-2];

int t,n;

scanf("%d",&t);

while(t--)

{scanf("%d",&n);

printf("%d\n",f[n]);

}

return 0;

}

典型的fibonacci序列

 

不容易系列之二

http://acm.hdu.edu.cn/showproblem.php?pid=2042

#include<cstdio>

int f(int i)

{

if(i==0)

return 3;

return f(i-1)*2-2;

}

int main()

{

int n;

scanf("%d",&n);

while(n--)

{

int a;

scanf("%d",&a);

printf("%d\n",f(a));

}

return 0;

}

 

简单的递归题,懒得写成迭代形式。

 

一只小蜜蜂...

http://acm.hdu.edu.cn/showproblem.php?pid=2044

#include<cstdio>

int main()

{

int n;

long long f[50];

f[1]=1;f[2]=1;f[3]=2;

for(int i=4;i<50;++i)

{

f[i]=f[i-1]+f[i-2];

}

int a,b;

scanf("%d",&n);

while(n--)

{

scanf("%d%d",&a,&b);

printf("%I64d\n",f[b-a+1]);

}

return 0;

}

一个路径的可能数=他前面两个路径之和。

 

 

阿牛的EOF牛肉串

http://acm.hdu.edu.cn/showproblem.php?pid=2047

#include<cstdio>

int main()

{

int n;

long long f[40];

f[1]=3;f[2]=8;

for(int i=3;i<40;++i)

f[i]=f[i-1]*2+2*f[i-2];

while(scanf("%d",&n)!=EOF)

{

printf("%I64d\n",f[n]);

}

return 0;

}

分两种情况讨论,一种是最后一个为EF,一种是最后一个为O,则倒数第二个肯定为EF。

 

神、上帝以及老天爷

http://acm.hdu.edu.cn/showproblem.php?pid=2048

#include<cstdio>

#include<cmath>

int main()

{

int n;

long long s=1;

long long f[21];

long long g[21];

f[2]=1;

f[3]=2;

g[2]=2;

g[3]=6;

for(int i=4;i<=20;++i)

{

f[i]=(i-1)*f[i-1]+(i-1)*f[i-2];

g[i]=g[i-1]*i;

}

int t;

scanf("%d",&t);

while(t--)

{

scanf("%d",&n);

printf("%.2lf%%\n",double(f[n])/g[n]*100);

}

return 0;

}

错位排列 假设第一个位置被除1以为的占据,则剩下的序列可分为:

第一个位置被1占据和不被1占据两种情况。

从而f[n]=(n-1)*f[n-1]+(n-1)*f[n-2];

 

不容易系列之(4)——考新郎

http://acm.hdu.edu.cn/showproblem.php?pid=2049

#include<cstdio>

int main()

{

long long  f[21],g[21];

f[1]=0;f[2]=1;f[3]=2;g[1]=1;g[2]=2;g[3]=6;g[0]=1;

for(int i=4;i<=20;++i) 

{

f[i]=(i-1)*(f[i-1]+f[i-2]);

g[i]=g[i-1]*i;

}

int n;

scanf("%d",&n);

while(n--)

{

int m,n;

scanf("%d%d",&n,&m);

printf("%I64d\n",g[n]/g[n-m]/g[m]*f[m]);

}

return 0;

}

 思想跟上题差不多。。

 

Counting Triangles

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1629

#include<iostream>

using namespace std;

int main()

{

int n;

int a[520]={0};

for(int i=1;i<=500;i++)

{

a[i]=a[i-1]+(i+1)*i/2+((i-1)+1)*i/4;

}

while(cin>>n)

{

cout<<a[n]<<endl;

}

return 0;

}

说明:由小到大推导,n可以由n-1推导出来:

a[n]=a[n-1]+(n+1)*n/2+((n-1)+1)*n/4

n长的三角形由n-1长的三角形 加上1行三角形组成,我们只需考虑,加上1行后 生成的三角形数就可以了,(n+1)*n/2是计算生成上三角形的数目,((n-1)+1)*n/4是计算生成下三角形的数目。

本来我是这样写的:

a[i]=a[i-1]+(i+1)*i/2+(n%2?((i-1)+2)*i/4:(i-1)+1)*i/4)

但反而出错,可能是由于整数计算时约掉了小数部分的缘故。

 

Steps

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1871

#include<iostream>

#include<cmath>

using namespace std;

int main()

{

long long x,y;

long long dif;

while(cin>>x>>y)

{

dif=y-x;

if(dif==0)//考虑0的情况

{

cout<<0<<endl;

continue;

}

long long r=ceil(sqrt(double(dif)));//平方后向上取整

long long m=1+(r-1)*2;

if(r*r-dif>=r)

m--;

cout<<m<<endl;

}

return 0;

}

说明:主要是规律的查找和公式的推导

1 1

2 2

 

3 3

4 3

5 4

6 4

 

7 5

8 5

9 5

10 6

11 6

12 6

由以上的规律 推导出公式即可,我们可以看到 9开方为3 恰好是 连续值为5的个数,而5恰好=(3-1)*2+1,5~9开方取上整皆为 3,对于9和9之前的三个数,可以直接去(3-1)*2+1,对于7之前的两个数 (3-1)*2+1减一就能得到答案。

同理 2~4,13~16也是如此。

最后还得考虑0这个特殊情况。

这道题虽归属于简单题,但值得收藏。

 

posted on 2013-03-22 16:16 PegasusWang 阅读(...) 评论(...) 编辑 收藏