Transcript Document

程序设计实习
习题课
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为例
1. 求出小数部分长度 – 3位
2. 去掉小数点,变成整数95.123 -> 95123
3. 求95123^12 =
54881562051773183019454189902534341
5715973535967221869852721
4. 重新点小数点
548815620517731830194541.8990253434
15715973535967221869852721
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 文件结构图
file1
file2
dir3
dir2
file1
file2
]
]
file4
dir1
]
file3
*
file2
file1
*
#
DATA SET 1:
ROOT
|
dir3
|
|
dir2
|
|
file1
|
|
file2
|
dir1
file1
file2
file3
file4
DATA SET 2:
ROOT
file1
file2
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
输入:
5551
1142
0000
输出:
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
01
10
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
31
231
31
321
10 2
1 2 3 4 5 6 7 8 9 10
样例输出
312
123
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(n1)*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)*(ni)]/a(n-i-1)
rn = n-i-1
rm = [m-a(0)*a(n-1)-a(1)*a(n-2)-…-a(i-1)*(ni)]%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 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
的最大子矩阵是
92
-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]