450 likes | 593 Views
程序设计实习. 习题课 http://ai.pku.edu.cn/cpp2010. 习题课. 2951 浮点数高精度求幂 2775 文件结构图 2787 算 24. 2951. 题目描述:有一个实数 R ( 0.0 < R < 99.999 ) , 要求写程序精确计算 R 的 n 次方。 n 是整数并且 0 < n <= 25 。 输入:输入包括多组 R 和 n 。 R 的值占第 1 到 第 6 列 , n 的值占第 8 和第 9 列。
E N D
程序设计实习 习题课 http://ai.pku.edu.cn/cpp2010
习题课 2951 浮点数高精度求幂 2775 文件结构图 2787 算24
2951 题目描述:有一个实数 R ( 0.0 < R < 99.999 ) ,要求写程序精确计算 R 的 n 次方。n 是整数并且 0 < n <= 25。 输入:输入包括多组 R 和 n。 R 的值占第 1 到 第 6 列, n 的值占第 8 和第 9 列。 输出:对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后后面不不要的 0 。如果输出是整数,不要输出小数点。
2951 95.123 12 0.4321 20 5.1234 15 6.7592 9 98.999 10 1.0100 12 548815620517731830194541.899025343415715973535967221869852721 .00000005148554641076956121994511276767154838481760200726351203835429763013462401 43992025569.928573701266488041146654993318703707511666295476720493953024 29448126.764121021618164430206909037173276672 90429072743629540498.107596019456651774561044010001 1.126825030131969720661201
2951 思路:以95.123 12为例 • 求出小数部分长度 – 3位 • 去掉小数点,变成整数95.123 -> 95123 • 求95123^12 = 548815620517731830194541899025343415715973535967221869852721 • 重新点小数点548815620517731830194541.899025343415715973535967221869852721
2951 代码 #include<stdio.h> #include<string.h> const int MAX=100; char s[7]; int a[MAX],e,p,b,be,en,i; void mul() { int i,w=0; for(i=0;i<MAX;i++) { a[i]=a[i]*b+w; w=a[i]/10; a[i]=a[i]-w*10; } }
int main() { while (scanf("%s %d",s,&e)!=EOF) { memset(a,0,sizeof(a)); b=0; //Step 1 and Step 2 for(i=0;i<strlen(s);i++) if (s[i]=='.') p=strlen(s)-i-1; else b=b*10+s[i]-'0'; a[0]=1; //Step 3 for(i=0;i<e;i++) mul(); p*=e; //Step 4 for (be=MAX-1;a[be]==0&&be>=p-1;be--); for (en=0;a[en]==0&&en<p;en++); for (i=be;i>=p;i--) printf("%d",a[i]); if (en<p) printf("."); for (i=p-1;i>=en;i--) printf("%d",a[i]); printf("\n"); } return 0; }
2775 文件结构图 DATA SET 1: ROOT | dir3 | | dir2 | | file1 | | file2 | dir1 file1 file2 file3 file4 DATA SET 2: ROOT file1 file2 file1 file2 dir3 dir2 file1 file2 ] ] file4 dir1 ] file3 * file2 file1 * #
2775 关键:用什么样数据结构存储“文件结构图” struct DIR { char name[20]; int nf; int parent; char* files[MAXF]; bool children[MAXD]; } dirs[MAXD];
2775 思路: 1.读入数据,生成dirs[ ] 2.遍历dirs[ ],输出结果 技术问题: 一个文件或者目录的父(上层)目录一定是最近的没有被”]”闭合的目录
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define MAXD 200 #define MAXF 200 struct DIR { char name[20]; int nf; int parent; char* files[MAXF]; bool children[MAXD]; } dirs[MAXD]; int current = 0; int parent = 0; int data_set = 1; void new_dir(int x, char* tmp, int p) { sprintf(dirs[x].name, "%s", tmp); dirs[x].nf = 0; dirs[x].parent = p; memset(dirs[x].children, false, sizeof(dirs[x].children)); }
void clear() { current = 1; parent = 0; new_dir(0, "ROOT", -1); } void insert_file(int parent, char* tmp) { dirs[parent].files[dirs[parent].nf] = new char[20]; strcpy(dirs[parent].files[dirs[parent].nf], tmp); dirs[parent].nf++; } void format_print(int dep, char* s) { for(int i = 0; i < dep; i++) printf("| "); printf("%s\n", s); } int mycompare(const void *e1, const void *e2){ return strcmp(*(char **)e1,*(char **)e2); }
void work(int x, int dep) { if(x == 0) { printf("DATA SET %d:\n", data_set); } format_print(dep, dirs[x].name); for(int i = 0; i < MAXD; i++) if(dirs[x].children[i]) { work(i, dep+1); } qsort(dirs[x].files, dirs[x].nf, sizeof(dirs[x].files[0]), mycompare); for(int i = 0; i < dirs[x].nf; i++) format_print(dep, dirs[x].files[i]); }
int main() { clear(); //parent=0; current = 1; while(true) { char tmp[20]; scanf("%s", tmp); if(tmp[0] == '#') break; if(tmp[0] == '*') { work(0, 0); printf("\n"); clear(); data_set++; } if(tmp[0] == 'f') { insert_file(parent, tmp); } if(tmp[0] == 'd') { new_dir(current, tmp, parent); dirs[parent].children[current] = true; parent = current; current++; } if(tmp[0] == ']') { parent = dirs[parent].parent; } } return 0; }
2787 算24 输入: 5 5 5 1 1 1 4 2 0 0 0 0 输出: YES NO
2787 算24 子问题:k个数算24 递归:从k个数算24,规约到k-1个数算24 方法:从k个数中选2个数a和b,及一种运算op;将a和b从k个数中删除,再将(a op b)加入
#include<iostream> #include<cmath> using namespace std; bool right; double ele[4]; int main() { while(cin>>ele[0]>>ele[1]>>ele[2]>>ele[3]) { if(ele[0]==0||ele[1]==0||ele[2]==0||ele[3]==0) break; else if(cal(4)==1) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }
bool cal(int count) { int i,j; double temp1,temp2; if(count==1) if(ele[0]<24.001&&ele[0]>23.999) return true; else return false; for(i=0;i<count-1;i++) { for(j=i+1;j<count;j++){ temp1=ele[i]; temp2=ele[j]; ele[i]=temp1+temp2; ele[j]=ele[count-1]; if(cal(count-1)) return true; ele[i]=temp1-temp2; if(cal(count-1)) return true;
ele[i]=temp2-temp1; if(cal(count-1)) return true; ele[i]=temp1*temp2; if(cal(count-1)) return true; if(temp2!=0) { ele[i]=temp1/temp2; if(cal(count-1)) return true; } if(temp1!=0) { ele[i]=temp2/temp1; if(cal(count-1)) return true; } ele[i]=temp1; ele[j]=temp2; } } return false; }
2791 矩形覆盖 1833 排列 2950 摘花生 1095 Trees Made to Order 2766 最大子矩阵
2791 矩形覆盖 题目描述: 在平面上给出了n个点,现在需要用一些平行于坐标轴的矩形把这些点覆盖住。每个点都需要被覆盖,而且可以被覆盖多次。每个矩形都至少要覆盖两个点,而且处于矩形边界上的点也算作被矩形覆盖。注意:矩形的长宽都必须是正整数,也就是说矩形不能退化为线段或者点。 现在的问题是:怎样选择矩形,才能够使矩形的总面积最小。 输入: 输入包括多组测试数据。每组测试数据的第一行给出n (2 <= n <= 15),表示平面上的点数。后面的n行,每行上包括两个整数x, y (-1000 <= x, y <= 1000),给出一个点在平面上的x坐标和y坐标。输入数据保证:这n个点在平面上的位置各不相同。 最后一组测试数据中n = 0,表示输入的结束,这组数据不用处理。 输出: 对每一组测试数据,输出一行,包括一个正整数,给出矩形的最小总面积。
2791 样例输入: 2 0 1 1 0 0 样例输出: 1
2791 子问题:对于点集P, 求出覆盖P所用的矩形的最小面积和 - least(P) 规约:least(P) = min{least(Q) + least(R), S(P)} 其中Q∪R = P, S(P)为用一个矩形覆盖P的最小面积 问题规模:P的所有子集数目,2^|P|
2791 - TLE 能否缩减枚举空间,同时保证不漏掉最优解? 定义:如果对于点集P, least(P)==S(P), 则称P是紧密的。即如果覆盖点集P的最优方案是用一个矩形去覆盖,则P是紧密的。 Q和R中至少有一个是紧密的
2791 - TLE 对于确定的P,如何找到所有的(Q, R)使得Q是紧密的,且Q∪R = P ? 不好确定…换个思路: 对于确定的一个紧密的Q,枚举所有的R,用least(Q)+least(P)更新least(Q ∪ R)
2791 – 状态表示 对于n个点的点集P,用n位2进制数来表示P的所有子集。 例如P={p2, p1, p0},用 1=(001)2表示{p0}, 2=(010)2表示{p1}, 3=(011)2表示{p1, p0} 7=(111)2表示{p2, p1, p0} least(P) = least(7)
#include<stdio.h> int x[15], y[15]; int area[1<<15]; int least[1<<15]; int m; int main() { int num,i,j,xs,ys,xl,yl,size,a,b; while(1) { scanf("%d", &num); if(!num) break; for( i=0 ; i<num ; i++ ) scanf("%d%d", x+i, y+i); size = (1<<num) ; for( i=1 ; i<size ; i++ ) least[i] = 2000 * 2000 ; least[0]=0;
for( i=1 ; i<size ; i++ ) { xs=ys=1000; xl=yl=-1000; for( j=0 ; j<num ; j++ ) if( (i>>j)&1 ) { if( x[j]>xl ) xl=x[j]; if( x[j]<xs ) xs=x[j]; if( y[j]>yl ) yl=y[j]; if( y[j]<ys ) ys=y[j]; } a=xl-xs; b=yl-ys; if(a==0) a=1; if(b==0) b=1; area[i]=a*b; } area[0]=least[0]=0;
for(i = 1; i < size; i++) if(i & (i - 1)) least[i] = area[i]; for(i = 1; i < size; i++) if(least[i] == area[i]) { for(int j = 1; j < size; j++) if(least[i | j] > least[i] + least[j]) least[i | j] = least[i] + least[j]; } printf("%d\n", least[size-1]); } return 0; }
1833 – 排列 题目描述 大家知道,给出正整数n,则1到n这n个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1六个排列。 给出某个排列,求出这个排列的下k个排列,如果遇到最后一个排列,则下1排列为第1个排列,即排列1 2 3…n。 比如:n = 3,k=2 给出排列2 3 1,则它的下1个排列为3 1 2,下2个排列为3 2 1,因此答案为3 2 1。 输入 第一行是一个正整数m,表示测试数据的个数,下面是m组测试数据,每组测试数据第一行是2个正整数n( 1 <= n < 1024 )和k(1<=k<=64),第二行有n个正整数,是1,2 … n的一个排列。 输出 对于每组输入数据,输出一行,n个数,中间用空格隔开,表示输入排列的下k个排列。
1833 – 排列 样例输入 3 3 1 2 3 1 3 1 3 2 1 10 2 1 2 3 4 5 6 7 8 9 10 样例输出 3 1 2 1 2 3 1 2 3 4 5 6 7 9 8 10
1833 思路 while(k--) next_permutation(); 1.从右向左找到第一个下降的元素a[i],如果没有sort(a[1]…a[n]) 2.从a[i+1]…a[n]中找到最小的比a[i]大的元素a[j] 3.swap(a[i], a[j]) 4.sort(a[i+1]…a[j])
2950 摘花生 为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”
2950 摘花生 路径唯一,只需判断:如果超过限定时间跳出 为什么AC的人那么少?
1095 – Trees Made to Order The empty tree is numbered 0.The single-node tree is numbered 1.All binary trees having m nodes have numbers less than all those having m+1 nodes.Any binary tree having m nodes with left and right subtrees L and R is numbered n such that all trees having m nodes numbered > n have either Left subtrees numbered higher than L, or A left subtree = L and a right subtree numbered higher than R.
样例输入 1 20 31117532 0 样例输出 X ((X)X(X))X (X(X(((X(X))X(X))X(X))))X(((X((X)X((X)X)))X)X)
1095 子问题:输出编号为k的二叉树 归约:对于编号为k的二叉树,如何求出它的左子树编号l以及右子树编号r? 如果可以求出l及r: ( 输出l)X(输出r) 为了叙述方便,用a(n)表示包含n个节点的二叉树的数目 a(0)=1 a(n)=a(0)*a(n-1)+a(1)*a(n-2)+…+a(i)*a(n-i-1)+…+a(n-1)*a(0) 太复杂了!
1095 另一种子问题表示 子问题:输出在所有节点数为n的二叉树中,编号为m的二叉树T(n,m) 对于输入的k,如何求出对应的n,m? 归约:对于T(n,m),如何求出左子树T(ln,lm)及右子树T(rn,rm) n=最小的n0,使a(0)+a(1)+a(2)…+a(n0) >= k m = k-a(0)-a(1)-a(2)-…-a(n-1)-1, 从0开始编号
1095 对于T(n,m),如何求出左子树T(ln,lm)及右子树T(rn,rm)? a(0)*a(n-1)+a(1)*a(n-2)+…a(i)*a(n-i-1) >= m+1 ln = i lm = [m-a(0)*a(n-1)-a(1)*a(n-2)-…-a(i-1)*(n-i)]/a(n-i-1) rn = n-i-1 rm = [m-a(0)*a(n-1)-a(1)*a(n-2)-…-a(i-1)*(n-i)]%a(n-i-1)
#include<iostream> using namespace std; long a[20],n,num; int main() { a[0]=a[1]=1; for(int i=2;i<20;i++) for(int j=0;j<i;j++) a[i]+=a[j]*a[i-j-1]; long sum; cin>>num; while(num!=0) { sum=0;n=1; while(sum<num)sum+=a[n++]; n--; go(n,num-(sum-a[n])-1); cout<<endl; cin>>num; } return 0; }
void go(int n,int num) { long sum=0; for(int i=0;i<n;i++) { if(sum+a[i]*a[n-1-i]>=num+1) { if(i!=0) { cout<<'('; go(i,(num-sum)/a[n-1-i]); cout<<')'; } cout<<'X'; if(n-1-i!=0) { cout<<'('; go(n-1-i,(num-sum)%a[n-1-i]); cout<<')'; } break; } sum+=a[i]*a[n-1-i]; } }
2766 最大子矩阵 已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵。比如,如下4 * 4的矩阵0 -2 -7 09 2 -6 2-4 1 -4 1-1 8 0 -2的最大子矩阵是9 2-4 1-1 8这个子矩阵的大小是15
2766 一维问题:对于数列X1, X2, X3 … Xn 求出和最大的自区间 子问题:以Xi结尾的区间的最大和f(i) f(i) = max{f(i-1) + Xi, Xi} DP求解
2766 二维问题: 枚举矩形的左右边界,转变为一维问题 边界[i, r] X1 = a[1][i]+…+a[1][r] X2 = a[2][i]+…+a[2][r] …… Xn = a[n][i]+…+a[n][r] 于是:求X1, X2, … , Xn的最大子区间和
2766 利用部分和优化 a[k][i]+…+a[k][j] = sum[k][j] – sum[k][i-1]