目录
蟠桃记 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
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
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
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
long long pre=0,f=0,t=-1;
for(int i=1;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 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是牌放横的情况。 http://acm.hdu.edu.cn/showproblem.php?pid=2045 #include 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,因为此时最后一个只能取一种可能 http://acm.hdu.edu.cn/showproblem.php?pid=1297 #include 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 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 根据直线分平面可知,由交点决定了射线和线段的条数,进而决定了新增的区域数。当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 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 的深度且相连的槽其深度之差不得为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 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 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 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 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 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; } 一个路径的可能数=他前面两个路径之和。 http://acm.hdu.edu.cn/showproblem.php?pid=2047 #include 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 #include 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]; http://acm.hdu.edu.cn/showproblem.php?pid=2049 #include 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; } 思想跟上题差不多。。 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1629 #include 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) { 骨牌铺方格
不容易系列之(3)—— LELE的RPG难题
Children’s Queue
献给杭电五十周年校庆的礼物
(2) 折线分平面(hdu2050)钥匙计数之二
钥匙计数之一
母牛的故事
超级楼梯
不容易系列之二
一只小蜜蜂...
阿牛的EOF牛肉串
神、上帝以及老天爷
不容易系列之(4)——考新郎
Counting Triangles