实验一 算法概述
- 数字统计问题
- 字典序问题
- 兔子问题
实验二 递归与分治策略及其应用
- 整数划分
- 有重复元素的排列问题
- 棋盘覆盖问题
实验三 动态规划实验
- 矩阵连乘问题
- 最大子段和问题
- 0-1背包
- 最长单调子序列
- 最长公共子序列(LCS)
实验一 算法概述
- 数字统计问题
任务描述
本关任务:一本书的页码从自然数1开始编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0.
例如,第8页用数字8表示,而不是08或者008之类。
数字统计问题要求对给定书的总页码n,计算出书的全部页码中分别出现了多少次数字0,1,2,…,9。
编程要求
根据提示,在右侧编辑器补充代码,计算并输出书的全部页码中分别出现数字0,1,2,…,9的次数。
测试说明
平台会对你编写的代码进行测试:
测试输入:11
预期输出:Please input the pagecount
11
The result is
1
4
1
1
1
1
1
1
1
1
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void count(int );
int main()
{
int pagecount;
int i;
printf("Please input the pagecount\n");
scanf("%d",&pagecount);
printf("%d\n",pagecount);
count(pagecount);
return 0;
}
void count(int pagecount)
{
int a=0,b=0,c=0,d=0,e=0,f=0,g=0,x=0,y=0,z=0;
for(int i=1;i<=pagecount;i++)
{
for(int j=i;j>0;j=j/10)
{
if(j%10==0)
a++;
if(j%10==1)
b++;
if(j%10==2)
c++;
if(j%10==3)
d++;
if(j%10==4)
e++;
if(j%10==5)
f++;
if(j%10==6)
g++;
if(j%10==7)
x++;
if(j%10==8)
y++;
if(j%10==9)
z++;
}
}
printf("The result is\n");
printf("%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n",a,b,c,d,e,f,g,x,y,z);
}
- 字典序问题
任务描述
详见教材算法实现题1的1-2字典序问题
在数据加密和数据压缩中长需要对特殊的字符串进行编码。给定的字母表由26个小写英文字母组成,即A={a,b,c,...,z}。该字母表产生的升序字符串是指字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字母最多出现1次。例如,a、b、ab、bc、xyz等字符串都是升序字符串。现在对字母表A产生的所有长度不超过6的升序字符串按照字典序排列并编码,编码详见教材。
请对于任意长度不超过6的升序字符串,迅速计算出它在字典中的编码。
编程要求
对于任意长度不超过6的升序字符串,迅速计算出它在字典中的编码。
算法设计:对于给定的长度不超过6的升序字符串,计算它在上述字典中编码。
数据输入说明:第一行输入数据k表示接下来有k行;在接下来的k行中,每行给出一个字符串;
测试说明
平台会对你编写的代码进行测试:
测试输入:2
a
b
预期输出:1
2
#include<stdio.h>
#include<string.h>
int f(int m,int n)
{
int sum=1,sum1=1;
int i;
for(i=1;i<=n;i++)
sum*=i;
for(i=m;i>m-n;i--)
sum1*=i;
return sum1/sum;
}
int main()
{
int t;
int sum;
int i,j,len;
char s[10];
int a[10];
scanf("%d",&t);
while(t--)
{
getchar();
sum=1;
scanf("%s",s);
len=strlen(s);
for(i=1;i<len;i++)
{
sum+=f(26,i); //小于字符串长度的所有情况
}
for(i=0;i<len;i++)
{
a[i]=s[i]-96; //计算每个字符从a开始的数值
// printf("%d ",a[i]);
}
//printf("%d\n",f(26,2));
int temp=1;
for(i=len;i>0;i--)
{
for(j=temp;j<a[len-i];j++)
{
sum+=f(26-j,i-1); //依次扫描字符,计算所有情况
}
temp=a[len-i]+1;
}
printf("%d\n",sum);
}
return 0;
}
- 兔子问题
任务描述
求解Fibonacci数列的第110项、第200项的值。
相关知识
算法时空复杂度
编程要求
求解Fibonacci数列的第110项、第200项的值
测试说明
平台会对你编写的代码进行测试:
测试输入:110
预期输出:26925748508234281076009
测试输入:200
预期输出:173402521172797813159685037284371942044301
提示:注意溢出及大数问题
第一项为 0 ,第二项为1
#include<stdio.h>
#define F1 300
#define F2 1000
int main()
{
int F[F1][F2]={0};
int k;
scanf("%d",&k);
F[0][F2-1]=0;
F[1][F2-1]=1;
for(int d=2;d<k;d++)
{
for(int i=F2-1;i>=0;i--)
{
F[d][i]=F[d-1][i]+F[d-2][i];
}
int r=F2-1;
while(r>=0){
if(F[d][r]>=10)
{
F[d][r-1]+=1;
F[d][r]=F[d][r]-10;
}
r--;
}
}
int j=0;
while(j<F2)
{
if(F[k-1][j]!=0)
{
while(j<F2)
{
printf("%d",F[k-1][j]);
j++;
}
}
j++;
}
return 0;
}
- 整数划分
任务描述
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。正整数n 的不同的划分个数称为正整数n 的划分数。
对给定的整数,输出对应的划分数。
相关知识
递归
分治
测试说明
平台会对你编写的代码进行测试:
测试输入:5
预期输出:7
测试输入:6
预期输出:11
提示:
对于样例一,可以划分的情况是:
5, 4+1, 3+2, 3+1+1, 2+2+1, 2+1+1+1, 1+1+1+1+1
故可以划分为7种
#include <stdio.h>
int sort(int n,int m)
{
if(m==0){
return 0;
}
if(n<m){
return sort(n,n);
}
if(n==m){
return sort(n,m-1)+1;
}
if(n>m){
return sort(n-m,m)+sort(n,m-1);
}
}
int main()
{
int n;
scanf("%d",&n);
printf("%d",sort(n,n));
return 0;
}
- 有重复元素的排列问题
任务描述
设集合R={r1,r2,...,rn}是要进行排列的n个元素,其中r1,r2,...,rn可能相同。
试着设计一个算法,列出R的所有不同排列。
即,给定n以及待排的n个可能重复的元素。计算输出n个元素的所有不同排列。
输入描述
第1行是元素个数n,1<=n<=500。接下来的1行是待排列的n个元素,元素中间不要加空格。
输出格式
程序运行结束时,将计算输出n个元素的所有不同排列。最后1行中的数是排列总数。
测试说明
平台会对你编写的代码进行测试:
测试输入:4
aacc
预期输出:aacc
acac
acca
caac
caca
ccaa
6
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void exchange(char &a, char &b) {
char temp = a;
a = b;
b = temp;
}
int findsame(char a[], int k, int m) {
for(int i = k; i < m; i++) {
if(a[i] == a[m]) return 1;
}
return 0;
}
void sort(char a[], int k, int m, int &count) {
if(k == m) {
for(int i = 0; i <= m; i++) {
printf("%c",a[i]);
}
printf("\n");
count++;
}else{
for(int i = k; i <= m; i++)
{
if (findsame(a,k,i) == 1)
continue;
exchange(a[k], a[i]);
sort(a, k + 1, m, count);
exchange(a[k], a[i]);
}
}
}
int main()
{
int n, i, sum = 0;
scanf("%d",&n);
char a[n+1];
scanf("%s",a);
sort(a, 0, n - 1, sum);
printf("%d",sum);
return 0;
}
- 棋盘覆盖问题
任务描述
在一个2k×2k个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
易知,覆盖任意一个2k×2k的特殊棋盘,用到的骨牌数恰好为(4K-1)/3。
输入格式
第一行为k(棋盘的尺寸),第二行为x,y(1<=x,y<=2^k),分别表示特殊方格所在行与列。
输出格式
共2^k行,分别表示覆盖该格的L型的编号(特殊格用0表示)。
输出要求:最小字符宽度为4,左对齐输出
参考代码:
样例
平台会对你编写的代码进行测试:
测试输入:3 3 2
预期输出:3 3 4 4 8 8 9 9
3 2 2 4 8 7 7 9
5 2 6 6 10 10 7 11
5 5 0 6 1 10 11 11
13 13 14 1 1 18 19 19
13 12 14 14 18 18 17 19
15 12 12 16 20 17 17 21
15 15 16 16 20 20 21 21
提示:
特殊骨牌的牌号为0
正常骨牌号从1开始
覆盖棋盘请严格遵循先覆盖左上角
,右上角
,左下角
,右下脚
的循序(具体可以参考算法书P21的代码及注释)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int title=1;
void ChessBoard(int** b,int x,int y,int dr,int dc,int size ){
if(size==1){
return ;
}
int t=title++;
int s=size/2;
//左上角
if(dr <x+s&&dc<y+s){
ChessBoard(b,x, y,dr, dc, s);
}
else{
b[x+s-1][y+s-1]=t;
ChessBoard(b,x, y,x+s-1, y+s-1, s);
}
//右上角
if(dr <x+s&&dc>=y+s){
ChessBoard(b,x, y+s,dr, dc, s);
}
else{
b[x+s-1][y+s]=t;
ChessBoard(b,x, y+s,x+s-1, y+s, s);
}
//左下角
if(dr >=x+s&&dc<y+s){
ChessBoard(b,x+s, y,dr, dc, s);
}
else{
b[x+s][y+s-1]=t;
ChessBoard(b,x+s, y,x+s, y+s-1, s);
}
//右下角
if(dr >=x+s&&dc>=y+s){
ChessBoard(b,x+s, y+s,dr, dc, s);
}
else{
b[x+s][y+s]=t;
ChessBoard(b,x+s, y+s,x+s, y+s, s);
}
}
int main(){
int m,x,y;
scanf("%d %d %d",&m,&x,&y);
m=pow(2,m);
int **b;
b=(int**)malloc(sizeof(int*)*m);
for(int i=0;i<m;i++){
b[i]=(int*)malloc(sizeof(int)*m);
}
b[x][y]=0;
ChessBoard(b,0,0,x,y,m);
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
if(j==m-1) {
printf("%d\n",b[i][j]);
}
else{ printf("%-4d",b[i][j]);
}
}
}
return 0;
}
- 矩阵连乘问题
任务描述
在计算矩阵连乘积时,加括号的方式对计算量有影响。
例如有三个矩阵A1,A2,A3连乘,它们的维数分别为10*100,100*5,5*50
。用第一种加括号方式(A1A2)A3计算,则所需数乘次数为10*100*5+10*5*50=7500
。用第二种加括号方式A1(A2A3)计算,需要100*5*50+10*100*50=75000
次数乘。
输入连乘矩阵的个数,每个矩阵的维数。要求输出最少数乘次数。
相关知识
输入格式
第一行输入一个n,代表有n个矩阵
接下来n行,每行输入两个数a,b,代表每个矩阵的维度。
0<n,a,b<100
输出格式
输出一个数,代表最小数乘次数。
输入
6 30 35
35 15
15 5
5 10
10 20
20 25
输出
15125
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int a[n][2];
int b[n][n]={0};
for(int i=0;i<n;i++){
scanf("%d %d",&a[i][0],&a[i][1]);
}
for(int i=1;i<n;i++){
for(int j=0;j<n-i;j++){
b[j][j+i]=b[j][j]+b[j+1][j+i]+a[j][0]*a[j][1]*a[j+i][1];
int k=j+1;
for(;k<j+i;k++){
int t=b[j][k]+b[k+1][j+i]+a[j][0]*a[k][1]*a[j+i][1];
if(t<b[j][j+i]) {
b[j][j+i]=t;
}
}
}
}
printf("%d",b[0][n-1]);
return 0;
}
- 最大子段和问题
任务描述
对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。
相关知识
动态规划
输入格式
第一行输入一个n,代表有n个数
下面一行输入n个数,代表序列
0<n<100
序列中的数的范围为[-2000,2000]
输出格式
输出一个数,代表最大字段和。
输入
6 -2 11 -4 13 -5 -2
输出
20
#include<stdio.h>
#include<math.h>
const int N =1e5+10;
int main()
{
int n;
scanf("%d",&n);
int a[N];
int max=0,s=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
s+=a[i];
if(s>max)
max=s;
if(s<0)
s=0;
}
printf("%d",max);
return 0;
}
// int main()
// {
// int n;
// scanf("%d",&n);
// int a[N],b[N];
// for(int i=1;i<=n;i++)
// {
// scanf("%d",&a[i]);
// b[i]=a[i];
// }
// int max=0;
// for(int i=1;i<=n;i++)
// {
// if(b[i-1]>0) b[i]=b[i-1]+a[i];
// else b[i]=fmax(b[i-1]+a[i],a[i]);
// if(b[i]>max) max=b[i];
// }
// printf("%d",max);
// return 0;
// }
- 0-1背包
任务描述
给定n个物品和一背包,物品i的重量是wi,其价值为vi,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
相关知识
动态规划
输入格式
第一行输入一个n,c,代表有n个物品背包容量为c
接下来n行,每行输入wi,和vi
其中0<n,c,wi,vi<5000
输出格式
输出一个数,代表最大价值。
输入
4 8
2 3
3 4
4 5
5 6
输出
10
//01背包问题
#include<stdio.h>
#include<math.h>
//using namespace std;
const int N=1e6+10;
int v[N];
int w[N];
int dp[N][N];
int main()
{
int n,c;
scanf("%d %d",&n,&c);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&w[i],&v[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=c;j++)
{
if(j<w[i]) dp[i][j]=dp[i-1][j];
else
dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
printf("%d",dp[n][c]);
return 0;
}
- 最长单调子序列
任务描述
给定一个序列,求这个序列的最长上升子序列的长度,并输出这个最长上升子序列,题目保证,最长上升子序列只有一个。
相关知识
最长上升子序列
输入格式
第一行输入一个n,代表序列长度
第二行输入n个值,代表这个序列
0<n<1000
-1000<序列内的数<1000
输出格式
第一行输出一个数,代表最长上升子序列的长度。
第二行打印这个子序列。
输入
8 5 2 8 6 3 6 5 7
输出
4 2 3 6 7
- 最长公共子序列(LCS)
任务描述
给定两个序列X=ABCBDAB, Y=BDCABA,求X和Y的最长公共子序列
相关知识
动态规划
输入格式
两行,每行为一个长度不超过500的字符串(全为大写字符串)
输出格式
输出一个数,最长公共子序列的长度,若不存在公共子序列,则输出0。
输入
ABCBDAB
BDCABA
输出
4