240 likes | 379 Views
TKS. 1. 高级语言. (第二十六讲). ( 编程选讲八 ). 铺满方格、 最少失约 、 语文教师的困惑 、 放苹果. 绍兴文理学院. 计算机系计算机应用教研室. 例 1 铺满方格 [1041]. (n1+n2+n3)!. n1!n2!n3!. TKS. 2. 有 1×n 的一个长方形,用 1×1 、 1×2 、 1×3 的骨牌铺满方格。例如当 n=3 时为 1×3 的方格(如图),此时用 1×1 , 1×2 , 1×3 的骨牌铺满方格,共有四种铺法。. Input 多组测试,每组一个整数 n ( 1<=n<=50 ),输入到文件尾结束。
E N D
TKS 1 高级语言 (第二十六讲) (编程选讲八) 铺满方格、最少失约、 语文教师的困惑、放苹果 绍兴文理学院 计算机系计算机应用教研室
例1 铺满方格[1041] (n1+n2+n3)! n1!n2!n3! TKS 2 有1×n的一个长方形,用1×1、1×2、1×3的骨牌铺满方格。例如当n=3时为1×3的方格(如图),此时用1×1,1×2,1×3的骨牌铺满方格,共有四种铺法。 Input 多组测试,每组一个整数n(1<=n<=50),输入到文件尾结束。 Output 每组测试输出一行,表示铺法数。 Sample Input 1 2 3 Sample Output 1 2 4 分析1:当1×1、1×2、1×3的骨牌分别有n1、n2、n3块时,则有 种铺法。 17:10
(n1+n2+n3)! (n1+n2+n3)! n(n-1)(n-2)┅(n1+1) n1!n2!n3! n1!n2!n3! n2!n3! TKS 3 分析2:对于 ,要注意到计算过程中存储的数据是 否会溢出,设n1+n2+n3=n,n1、n2、n3的最大值是n1,则: 分子也不要乘积,要用分母的各因子去除分子的个因子,使分子的积仅可能的小,最后再除分母可能有的大于1的因子。 结果或中间数据还可能出现int类型数据放不下的情况。其实,在测试数据中,最大的输出数据为:10562230626642(14位) 分析3:对于可能有较大的数值,可考虑用__int64类型的数据。 __int64是64位整数,其数值范围为: 17:10
定义:__int64 a; 定义:unsigned __int64 a; 使用:scanf(“%I64u”,&a); printf(“%I64u”,a); (n1+n2+n3)! n1!n2!n3! TKS 4 使用:scanf(“%I64d”,&a); printf(“%I64d”,a); 的值。 分析4:用一函数来计算 fact(n3,n3,n1) #include<iostream> using namespace std; __int64 fact(int a,int b,int c) {unsigned __int64 sum; int s[50],s1[50],s2[50],n,i,j,m,a1,b1,t; if(a>b) {t=a;a=b;b=t;} if(a>c) {t=a,a=c;c=t;} if(b>c) {t=b;b=c;c=t;} n=m=a+b+c;a1=a;b1=b; 17:10
for(i=0;i<n-c;i++) s[i]=m--; for(i=0;i<b-1;i++) s1[i]=b1--; for(i=0;i<a-1;i++) s2[i]=a1--; for(i=0;i<n-c;i++) {for(j=0;j<b-1;j++) if(s[i]%s1[j]==0&&s1[j]>1) {s[i]=s[i]/s1[j];s1[j]=1;} for(j=0;j<a-1;j++) if(s[i]%s2[j]==0) {s[i]=s[i]/s2[j];s2[j]=1;} } sum=1; for(i=0;i<n-c;i++) sum=sum*s[i]; for(j=0;j<b-1;j++) if(s1[j]>1&&sum%s1[j]==0) sum=sum/s1[j]; for(j=0;j<a-1;j++) if(s2[j]>1&&sum%s2[j]==0) sum=sum/s2[j]; return sum; } TKS 5 17:10
int main() { int n,n3,n2,n1,i,j; unsigned __int64 t; while(cin>>n) {n3=n/3;t=0; for(i=n3;i>=0;i--) {n2=(n-i*3)/2; for(j=n2;j>=0;j--) {n1=n-i*3-j*2; t=t+fact(i,j,n1); } } printf("%I64u\n",t); } return 0; } 例1 铺满方格[1041]C+26_1 TKS 6 testinput 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 testoutput 1 2 4 7 13 24 44 81 149 274 504 927 1705 3136 5768 10609 30 29 28 27 26 25 24 23 19 40 42 45 50 48 49 53798080 29249425 15902591 8646064 4700770 2555757 1389537 755476 66012 23837527729 80641778674 501774317241 10562230626642 3122171529233 5742568741225 17:10
分析与思考的收获 …… …… …… TKS 7 例1 铺满方格的第二种解法C+26_1_1 1×n的一个长方形用1×1,1×2,1×3的骨牌铺满方格,共包含以下三种情况: #include <iostream> using namespace std ; __int64 f(int n) {if(n==1) return 1; if(n==2) return 2; if(n==3) return 4; return f(n-1)+f(n-2)+f(n-3); } int main() { int n; while(cin>>n) printf( "%I64d\n", f(n)); return 0 ; } f(n-1) f(n-2) f(n-3) 得:f(n)=f(n-1)+f(n-2)+f(n-3) 17:10
还是分析与思考的收获 TKS 8 避免重复计算,事先算好各f(n)的值。 例1 铺满方格的第三种解法C+26_1_2 #include<iostream> using namespace std; int main() {__int64 s[51]={0,1,2,4}; int n,i; for(i=4;i<=50;i++) s[i]=s[i-1]+s[i-2]+s[i-3]; while(cin>>n) printf("%I64d\n",s[n]); return 0; } 17:10
例2 放苹果[1022] TKS 9 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法(不同的分法用K表示)? 5,1,1和1,5,1 是同一种分法。 Input 第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。 Output 对输入的每组数据M和N,用一行输出相应的K。 Sample Input 1 7 3 Sample Output 8 17:10
分析 TKS 10 对于m=7,n=3的情况,有以下几种可能的放法: 0 0 7 0 1 6 0 2 5 0 3 4 1 1 5 1 2 4 1 3 3 2 2 3 先确定第一个(也就是任意一个)盘中放苹果的数量,从最少(0)开始,由于不同盘中相同数量的苹果数是同一种放法,故,第一个盘中放苹果的数量只要考虑到从0个到m/n个即可; 由于后面盘中放的苹果数只要考虑不比在盘中已经放了的苹果数就可以了,设第一个盘中放了i个苹果,则还有m-i个苹果放在n-1个盘里,注意到前面的盘中已经放了i个苹果,后面的盘中要放的苹果数不比i小,即求函数f(m-i,n-1,i)的值,在f(m-i,n-1,i)中其方法是一样的。 所以对于0≤i≤m/n,可以写一个递归函数 f(m-i,n-1,i)实现之。 17:10
例2 放苹果[1022]C+26_2 int main() {int i,n,m,c,t,k,s; cin>>t; for(c=0;c<t;c++) {cin>>m>>n; if(n==1) {printf("1\n");continue;} k=m/n;s=0; for(i=0;i<=k;i++) s=s+f(m-i,n-1,i); printf("%d\n",s); } return 0; } TKS 11 #include<iostream> using namespace std; int f(int m,int n,int p) { int k,i,s=0; if(n==1) return 1; k=m/n; for(i=p;i<=k;i++) s=s+f(m-i,n-1,i); return s; } 测试数据C+26_2.DOC 17:10
例3 数字螺旋方阵 已知16个数字的螺旋方阵的形式如下所示。 16 15 14 13 5 4 3 12 6 1 2 11 7 8 9 10 输入数据的第一行为一个正整数T, 表示测试数据的组数。然后是T组测试数据。对于输入的n(n<=20),请输出n×n的螺旋方阵。各列按4位宽度输出。 Input 输入数据的第一行为一个正整数T, 表示测试数据的组数. 然后是T组测试数据。每组测试数据一个正整数n。 Output 输出n×n的数字螺旋方阵。各列按4位宽度输出。 TKS 12 17:10
Sample Input 2 5 6 TKS 13 Sample Output 25 24 23 22 21 10 9 8 7 20 11 2 1 6 19 12 3 4 5 18 13 14 15 16 17 36 35 34 33 32 31 17 16 15 14 13 30 18 5 4 3 12 29 19 6 1 2 11 28 20 7 8 9 10 27 21 22 23 24 25 26 17:10
int main() {int n,i,j,k,d,m,t,p,a[26][26]; cin>>t; for(p=0;p<t;p++) {cin>>n; for(i=1;i<=25;i++) for(j=1;j<=25;j++) a[i][j]=0; m=(n+1)/2;k=n*n+1; for(d=1;d<=m;d++) //圈数 {i=d; //上面一行 for(j=d;j<=n+1-d;j++) {k--; a[i][j]=k; } j=n-d+1; //右面一列 for(i=d+1;i<=n+1-d;i++) {k--; a[i][j]=k; } i=n-d+1; //下面一行 for(j=n-d;j>=d;j--) {k--; a[i][j]=k; } j=d; //左面一列 for(i=n-d;i>=d+1;i--) {k--; a[i][j]=k; } } for(i=1;i<=n;i++) //输出 {for(j=1;j<=n;j++) printf("%4d",a[i][j]); printf("\n"); } } return 0; } TKS 14 例3 数字螺旋方阵 C+26_3 17:10
例4 语文教师的困惑[1027] TKS 15 ACM星球上的单词至少由4个字母组成,并且规定:构成单词的字母必须是不降序的,即对于单词W1W2…Wn,必须满足W1<=W2<=…<=Wn(由ACSII顺序决定大小关系)。满足这些条件的单词就一定是合法的。例如:ACMR,ABCDEFXZ是合法单词,而ACM,ACMER则不是合法单词。 作为语文教师的你,需要经常修改学生的作业。当然,考虑到教师的工作量,你只需要划去若干字母,使得剩下的每个单词能符合合法性规定即可。给你一个句子,现在请你判断,最少需要删除多少字母,才符合语法要求。 Input 有多组测试.对于每组测试,首先是整数n(n>0),然后是n个需要你处理的单词(单词之间由一个空格分隔,其余全部由大写英文字母构成,不含其他符号)。 Output 对于每组测试,输出需要删除的最少字母数量。 Sample Input 3 ACMER IS ABCXYZ 4 ABCD EFGH EFFGH ABCD 4 EFGH EFEGH ABCDXY QWERTY 2 ABCA X Sample Output 3 0 3 5 17:10
分析: TKS 16 1、正确的输入:可用常规办法—cin方式输入数据; 2、编写一个函数来计算单词中要删除的字符数; 3、从局部到全部的方法来计算要删除的字符数: if(str[j]<=str[i]) d[i]=max(d[i],d[j]+1); str为单词的字符串;d[i]为str中下标为i的字符符合字母顺序的字母个数; 0≤i≤strlen-1, 0≤j≤i-1;// 从局部到全部的核心方法 #include <iostream> using namespace std; #include<string> int max(int a,int b) {return a>b?a:b; } 17:10
int fun(char str[]) { int i,j,len,d[200],ma; len=strlen(str); if(len<4) return len; d[0]=1;ma=1; for(i=1;i<len;i++) {d[i]=1; for(j=0;j<i;j++) if(str[j]<=str[i]) d[i]=max(d[i],d[j]+1); ma=max(ma,d[i]); } if(ma<4) return len; return len-ma; } int main() {int n,i,sum; char s[200]; while(cin>>n) {sum=0; for(i=0;i<n;i++) {cin>>s; sum=sum+fun(s); } printf("%d\n",sum); } return 0; } TKS 17 测试数据C+26_4.DOC 例4 语文教师的困惑[1027]C+26_4 17:10
例5 又见最少失约[1246] TKS 18 我们明年将有许多重要的活动需要参加,可是由于活动太多,无法全部参加,请你帮我们安排,以便更有效地参加活动,使得参加活动的价值总和最大。现在给出明年的全部活动的开始日期和结束日期,并给出这些活动对应的价值,请你计算能够得到的最大价值。 注意,由于活动是以天为基本单位,因此,能够参加的相邻两个活动必须满足:第二个活动的开始日期要晚于第一个活动的结束日期。 Input 输入数据首先包含一个整数T,表示测试实例的个数,然后是T组测试数据。每组数据第一行为整数n(1<=n<=300),代表有n个活动。以下的n行每行包括三个由空格隔开的整数i,j,k,其中1<=i<=j<=365,0<k<100.分别代表活动的开始日期、结束日期和价值。 Output Sample Input 2 3 2 5 5 4 6 4 6 7 10 Sample Output 15 10 3 1 2 3 2 4 10 3 10 2 对于每组测试数据,输出能够得到的最大价值总和,每组测试数据输出占一行。 17:10
#include <iostream> using namespace std; struct val {int d1; int d2; int v; int m; }; int max(int a,int b) {return a>b?a:b; } int main() {int n,i,j,t,k,ma; val s[300],s1; cin>>t; for(k=0;k<t;k++) {cin>>n; for(i=0;i<n;i++) cin>>s[i].d1>>s[i].d2>>s[i].v; for(i=0;i<n-1;i++) for(j=0;j<n-i-1;j++) if(s[j].d2>s[j+1].d2) {s1=s[j]; s[j]=s[j+1]; s[j+1]=s1; } TKS 19 17:10
ma=s[0].m=s[0].v; for(i=1;i<n;i++) {s[i].m=s[i].v; for(j=0;j<i;j++) if(s[j].d2<s[i].d1) s[i].m=max(s[i].m,s[j].m+s[i].v); ma=max(ma,s[i].m); } printf("%d\n",ma); } return 0; } TKS 20 例5 又见最少失约[1246]C+26_5 测试数据C+26_5.DOC 17:10
例6 0-1背包问题[1006] TKS 21 给定n种物品和1个背包,物品i的重量是wi,其价值为vi,背包的容量为C。要求选择装入背包的物品,使得装入背包中物品的总价值最大。 Input 每组测试数据包含3行,第1行为n和c,表示有n(0<=n<=400)个物品且背包容量为c (c<=1500),第二行为这n个物品的重量wi(1<=wi<=1000),第三行为这n个物品的价值vi。背包容量和物品重量都为整数。 Output 输出装入背包的最大总价值,每个答案一行。 Sample Input 5 10 2 2 6 5 4 6 3 5 4 6 Sample Output 15 17:10
分析: TKS 22 设置数组m[c],计算放入背包各容量(重量1~c)的最大价值; 对于每一对重量和价值的物品,递推计算放入背包各重量为j时m[j](1≤j≤c)的最大价值,要用逆序递推,否则会重复计算: for(i=0;i<n;i++) for(j=c;j>=1;j--) if(j>=w[i]) m[j]=max(m[j],m[j-w[i]]+v[i]); 如:5 10 2 2 6 5 4 6 3 5 4 6 各m[j]值为: w[0]=2 m[10]=6 m[9]=6 m[8]=6 m[7]=6 m[6]=6 m[5]=6 m[4]=6 m[3]=6 m[2]=6 m[1]=0 w[1]=2 m[10]=9 m[9]=9 m[8]=9 m[7]=9 m[6]=9 m[5]=9 m[4]=9 m[3]=6 m[2]=6 m[1]=0 w[2]=6 m[10]=14 m[9]=11 m[8]=11 m[7]=9 m[6]=9 m[5]=9 m[4]=9 m[3]=6 m[2]=6 m[1]=0 w[3]=5 m[10]=14 m[9]=13 m[8]=11 m[7]=10 m[6]=9 m[5]=9 m[4]=9 m[3]=6 m[2]=6 m[1]=0 w[4]=4 m[10]=15 m[9]=15 m[8]=15 m[7]=12 m[6]=12 m[5]=9 m[4]=9 m[3]=6 m[2]=6 m[1]=0 最后m[c]中的值即为背包可容纳的最大价值。 17:10
#include<iostream> using namespace std; int max(int a,int b) {return a>b?a:b;} int main() {int n,c,i,j,w[1000],v[1000],m[1501]; while(cin>>n>>c) {for(i=0;i<n;i++) cin>>w[i]; for(i=0;i<n;i++) cin>>v[i]; for(i=0;i<=c;i++) m[i]=0; for(i=0;i<n;i++) for(j=c;j>=1;j--) if(j>=w[i]) m[j]=max(m[j],m[j-w[i]]+v[i]); printf("%d\n",m[c]); } return 0; } TKS 23 例6 0-1背包问题[1006]C+26_6 测试数据C+26_6.DOC 17:10