数据结构作业2——2020
问题 A: Hanoi双塔问题
题目描述
给定A,B,C三根足够长的细柱,在A柱上放有2n个中间有空的圆盘,共有n个不同的尺寸,每个尺寸都有两个相同的圆盘,注意这两个圆盘是不加区分的(下图为n=3的情形)。现要将 这些国盘移到C柱上,在移动过程中可放在B柱上暂存。要求:
(1)每次只能移动一个圆盘;
(2) A、B、C三根细柱上的圆盘都要保持上小下大的顺序;
任务:设An为2n个圆盘完成上述任务所需的最少移动次数,对于输入的n,输出An。
输入
输入文件hanoi.in为一个正整数n,表示在A柱上放有2n个圆盘。
输出
输出文件hanoi.out仅一行,包含一个正整数,为完成上述任务所需的最少移动次数An。
样例输入
1
样例输出
2
提示
对于50%的数据, 1<=n<=25
对于100% 数据, 1<=n<=200
设法建立An与An-1的递推关系式。
代码
#include<iostream>
#include<cstring>
using namespace std;
/*
本题的思路和单个汉诺塔一样,第一个移动两次,第二个是移动六次,第n个可得到函数F(n)=2F(n-1)+2
本题还要进行高精度算法的实现 ,到n=200时都已经快百位的数字了,不这么弄OJ系统直接提示答案错误25%。
代码中的代码注释会更好的帮助你理解
*/
int main()
{ int i,n,temp,a[100]; //数组a是用来保留高精度算法中的各个位的数字,仅限在n<=200内成功,要是大于的话自己看着办吧
memset(a,0,sizeof(a)); //第一步使数组a中所有数据为0,不这么弄各种诡异的结果就会出现 ,还有写上这个必须加上#include<cstring>,否则报错 。
cin>>n; a[0]=2;
int len=1; //len的意思是会存在几位 ,比如n=3时答案是14,那么len=2
n--;
while(n--)
{
temp=0;
for(i=0;i<len;i++) //高精度算法,temp在进位时运用
{ a[i]=a[i]*2+temp;
if(i==0) a[i]+=2; //根据公式,每一遍循环中个位都要加2
temp=a[i]/10; //进位的话temp就变为1,不进位就仍为0
//比如n=3->n=4时14的个位4先乘2后加2,得到a[0]=10,那么temp=1到十位上时a[1]=1*2+1=3 ,得到30
a[i]%=10; //取余
}
a[len]+=temp;
if(a[len])len++;
}
for(int i=len-1;i>=0;i--){ cout<<a[i]; }
return 0;
}
问题 B: 哈夫曼树
题目描述
哈夫曼树,第一行输入一个数n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出所有结点的值与权值的乘积之和。
输入
输入有多组数据。
每组第一行输入一个数n,接着输入n个叶节点(叶节点权值不超过100,2<=n<=1000)。
输出
输出权值。
样例输入
2
2 8
3
5 11 30
样例输出
10
62
代码
#include <iostream>
#include <queue>
#include <functional>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
priority_queue<int,vector<int>,greater<int> > qq; //采用小根堆,greater<int>指堆排序后为递减的,即为小根堆
int num;
for (int i = 0; i < n; ++i)
{
cin >> num;
qq.push(num);
}
int sum = 0;
int a, b;
while (qq.size() >= 2)
{
a = qq.top();
qq.pop();
b = qq.top();
qq.pop();
qq.push(a + b);
sum += a + b;
}
cout << sum << endl;
}
return 0;
}
问题 C: 算法:折半插入排序
题目描述
折半插入排序同样是一种非常简单的排序方法,它的基本操作是在一个已经排好序的有序表中进行查找和插入。不难发现这个查找的过程可以十分自然的修改成折半查找的方式进行实现。
折半插入排序的算法可以描述如下:
在本题中,读入一串整数,将其使用以上描述的折半插入排序的方法从小到大排序,并输出。
输入
输入的第一行包含1个正整数n,表示共有n个整数需要参与排序。其中n不超过1000。
第二行包含n个用空格隔开的正整数,表示n个需要排序的整数。
输出
只有1行,包含n个整数,表示从小到大排序完毕的所有整数。
请在每个整数后输出一个空格,并请注意行尾输出换行。
样例输入
10
2 8 4 6 1 10 7 3 5 9
样例输出
1 2 3 4 5 6 7 8 9 10
提示
在本题中,需要按照题目描述中的算法完成折半插入排序的算法。与直接插入排序算法不同,折半插入排序算法在查找插入位置时采用了折半查找的方案,减少了关键字之间的比较次数,但是记录的移动次数并没有发生改变,因此折半插入排序的时间复杂度依旧为O(n2),同样不是一种非常高效的排序方法。
代码
#include<iostream>
using namespace std;
void BInsertSort(int n,int a[])
{
int mid,length=n;
for(int i=2;i<=n;++i)
{ a[0]=a[i];
int low=1,high=i-1;
while(low<=high)
{
mid=(low+high)/2;
if(a[0]<a[mid])
{high=mid-1;}
else
{low=mid+1;}
}
for(int j=i-1;j>=high+1;--j)
{a[j+1]=a[j];}
a[high+1]=a[0];
}
}
int main()
{ int num;
cin>>num;
int a[1001];
for(int i=1;i<=num;i++)
{cin>>a[i];}
BInsertSort(num,a);
for(int i=1;i<=num;i++)
{cout<<a[i]<<" ";}
return 0;
}
问题 D: 二叉树问题
题目描述
现给定一棵二叉树的先序遍历序列和中序遍历序列,要求你计算该二叉树的高度。
输入
输入包含多组测试数据,每组输入首先给出正整数N(<=50),为树中结点总数。下面2行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。
输出
对于每组输入,输出一个整数,即该二叉树的高度。
样例输入
9
ABDFGHIEC
FDHGIBEAC
7
Abcdefg
gfedcbA
样例输出
5
7
代码
#include<iostream>
#include<cstring>
using namespace std;
int calculate(char* xian,char* zhong,int n) //求二叉树的高度
{ if(n == 0) { return 0;} //若没有结点,为空树;但实际上这个步骤可以省略了,因为输入时限定不能输入0
int i;
for(i = 0; i < n; i++)
{ if(zhong[i] == xian[0]) //找到根结点在中序的位置
{break;}
}
/*之后根据先序和中序的特点,我们就可以隔开分治了
在先序序列中,下标1~i是该根节点左树的范围
在中序序列中,i+1到之后的n-i-1是右树范围
*/
int left = calculate(xian+1,zhong,i); //左子树的深度,使用递归
int right = calculate(xian+i+1,zhong+i+1,n-i-1);//右子树的深度,使用递归
return max(left,right)+1; //返回左右子树深度的较大值中的较大值+根结点的1次
}
int main()
{ int n;
while(cin>>n&&n)
{
char xian[n+1],zhong[n+1]; //先序数组和中序数组
cin >> xian >> zhong; //输入相应的字符串
cout << calculate(xian,zhong,n) << endl;//输出答案
memset(xian,' ',sizeof(xian)); //清空数组,准备下一次接收
memset(zhong,' ',sizeof(zhong));
}
return 0;
}
问题 E: 最短路
题目描述
给一张无向图G(U, E), 询问任意两点的最短距离。
输入
第一行两个整数n,m表示图中结点数和边的数量, 结点从1到n编号。
接下来m行,每行三个整数u,v,w表示u,v之间有一条距离为w的边。
接下来一行一个整数q,表示询问次数。
接下来q行每行两个整数u,v,表示询问u到v的最短距离, 如果u不能到达v输出-1。
数据范围:n <= 100, m <= 5000, q <= 10000, 0 < w <= 1000。
输出
对应输出q行答案。
样例输入
5 10
1 2 1
2 5 10
1 3 2
1 4 4
1 5 6
2 3 4
2 4 3
3 4 1
3 5 4
4 5 2
5
1 4
3 5
2 3
4 2
5 2
样例输出
3
3
3
3
5
代码
#include<iostream>
#include<cstring>
using namespace std;
const int temp=0x3f3f3f3f;
int arr[400][400];//这题在OJ系统里给的n的范围并不是0~100,坑死人了
int n,m;//n为点的个数,m为边的条数
int main()
{ while(cin>>n>>m)
{ for(int i=0;i<400;i++)//第一步,我们将整个二维数组的元素设置为无穷大或0
{ for(int j=0;j<400;j++)
{ arr[i][j]=temp;
if(i==j){arr[i][j]=0;}
}
}
int a,b,c;
for(int i=1;i<=m;i++)//第二步,我们将测试用例中的边的数据进行记录
{ cin>>a>>b>>c;
if(c<arr[a][b])
{arr[a][b]=arr[b][a]=c;}
}
for(int k=1;k<=n;k++)//第三步,弗洛伊德算法
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(arr[i][j]>arr[i][k]+arr[k][j]&&arr[i][k]<temp&&arr[k][j]<temp)
arr[i][j]=arr[i][k]+arr[k][j];
int q;
while(cin>>q&&q>=0)
{ for(int i=1;i<=q;i++)
{
int x,y;
cin>>x>>y;
if(arr[x][y]>=temp)
{cout<<"-1"<<endl;}
else
{cout<<arr[x][y]<<endl;}
}
}
}
return 0;
}
问题 F: Turn Off The Light
题目描述
There are n lights aligned in a row. These lights are numbered 1 to n from left to right. Initially some of the lights are turned on. Chiaki would like to turn off all the lights.
Chiaki starts from the p-th light. Each time she can go left or right (i.e. if Chiaki is at x, then she can go to x−1 or x+1) and then press the switch of the light in that position (i.e. if the light is turned on before, it will be turned off and vise versa).
For each p=1,2,…,n, Chiaki would like to know the minimum steps needed to turn off all the lights.
输入
There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:
The first line contains an integer n (2≤n≤106) -- the number of lights.
The second line contains a binary string s where si=1 means the i-th light is turned on and si=0 means i-th light is turned off.
It is guaranteed that the sum of all n does not exceed 107.
输出
样例输入
3
3
000
3
111
8
01010101
样例输出
0
26
432
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int INF = 1e9;
int f[maxn][2], g[maxn], h[maxn];
int n, b[maxn], z[maxn];
char s[maxn];
void solve() {
int l = n + 1, r = 0;
for(int i = 1; i <= n; ++i) if(b[i]) l = min(l, i), r = max(r, i);
if(l > r) {
for(int i = 1; i <= n; ++i) z[i] = 0;
return;
}
f[r][0] = 0, f[r][1] = 3;
if(1 < r) f[r - 1][0] = 1, f[r - 1][1] = 2;
for(int i = r - 2; i >= 1; --i) {
f[i][0] = f[i + 1][b[i + 1] ^ 1] + 1;
f[i][1] = f[i + 1][b[i + 1]] + 3;
}
g[l + 1] = 2, h[l + 1] = b[l + 1] ^ 1;
for(int i = l + 2; i <= r; ++i) {
if(h[i - 1]) g[i] = g[i - 1] + 2, h[i] = b[i] ^ 1;
else g[i] = g[i - 1] + 4, h[i] = b[i];
}
for(int i = 1; i <= l; ++i) z[i] = min(z[i], f[i][b[i]]);
for(int i = l + 1; i <= r; ++i) z[i] = min(z[i], g[i] + f[i][h[i]]);
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d %s", &n, s + 1);
for(int i = 1; i <= n; ++i) b[i] = s[i] - '0', z[i] = INF;
solve();
reverse(b + 1, b + 1 + n), reverse(z + 1, z + 1 + n);
solve();
reverse(z + 1, z + 1 + n);
LL ans = 0;
for(int i = 1; i <= n; ++i) ans = (ans + (LL) i * z[i]) % mod;
printf("%lld\n", ans);
}
return 0;
}