算法分析与设计实验exam
考察贪心算法,动态规划,深搜广搜,递归分治
实验题目🌴
No.1 二分查找 — noj1001
给定一个单调递增的整数序列,问某个整数是否在序列中。
输入
第一行为一个整数n,表示序列中整数的个数;第二行为n(n不超过10000)个整数;第三行为一个整数m(m不超过50000),表示查询的个数;接下来m行每行一个整数k。
输出
每个查询的输出占一行,如果k在序列中,输出Yes,否则输出No。
输入样例
5
1 3 4 7 11
3
3
6
9输出样例
Yes
No
No
之类就将所有的代码都写上,供阅读者验证
#include<iostream>
using namespace std;
bool binarySearch(int* arr, int num, int target)
{
int low = 0;
int high = num - 1;
while (low <= high)
{
int mid = low + (high - low) / 2;
if (arr[mid] < target)
{
low = mid + 1;
}
else if (arr[mid] > target)
{
high = mid - 1;
}
else
{
return true;
}
}
return false;
}
int main()
{
int n;
int arr[100];
int tnum;
int target[100];
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
cin >> tnum;
for (int i = 0; i < tnum; i++)
{
cin >> target[i];
}
for (int i = 0; i < tnum; i++)
{
if (binarySearch(arr, n, target[i]))
{
cout << "Yes\n";
}
else cout << "No\n";
}
}
这个算法题非常简单
接下来第二题看一下归并排序,其实归并排序和这里的二分查找都用了分治的策略
No.2 归并排序 — noj1002
归并排序的主要思想和合并两个有序链表思想一样,都是使用一个辅助数组来记录合并后的数据
给定一个数列,用归并排序算法把它排成升序。
输入
第一行是一个整数n(n不大于10000),表示要排序的数的个数;
下面一行是用空格隔开的n个整数。输出
输出排序后的数列,每个数字占一行。
输入样例
5
3 2 1 4 5输出样例
1
2
3
4
5
只是作为一个合格的programmer,需要注意题目所要求的范围,我之前就是将数组设置小了,导致一直运行时错误,这道题目的代码很简单,也就不详细分析了
只是用到了动态分配数组和删除数组delete
#include<iostream>
using namespace std;
int arr[10000]; //最开始数组给小了,一直都是运行时错误
void merge(int start, int mid, int end)
{
int i = start, j = mid + 1, k = 0;
int* temp = new int[end + 1 - start];
while (i <= mid && j <= end)
{
if (arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
temp[k++] = arr[j++];
}
while (i <= mid)
{
temp[k++] = arr[i++];
}
while (j <= end)
{
temp[k++] = arr[j++];
}
k = 0;
for (int i = start; i <= end; i++)
{
arr[i] = temp[k++];
}
delete[] temp;
}
void mergeSort(int start, int end)
{
if (start < end)
{
int mid = start + (end - start) / 2;
mergeSort(start, mid);
mergeSort(mid + 1, end);
merge(start, mid, end);
}
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
mergeSort(0, n - 1);
for (int i = 0; i < n; i++)
{
cout << arr[i] << endl;
}
}
接下来的第三题
No.3 0-1背包 — 1004
需对容量为c 的背包进行装载。从n 个物品中选取装入背包的物品,每件物品i 的重量为wi ,价值为pi 。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高。
输入
多个测例,每个测例的输入占三行。第一行两个整数:n(n<=10)和c,第二行n个整数分别是w1到wn,第三行n个整数分别是p1到pn。
n 和 c 都等于零标志输入结束。输出
每个测例的输出占一行,输出一个整数,即最佳装载的总价值。
输入样例
1 2
1
1
2 3
2 2
3 4
0 0输出样例
1
4
这里我的问题就是没有理解到题目的意思,是让先将所有的W输入之后才输入的P
这里使用while循环,再循环里使用背包问题的解法就可以了,其实这就是回溯算法的子集树结构
注意是从1开始循环的,不然会溢出报错,还有就是j是j–
#include<iostream>
using namespace std;
int w[1000];
int p[1000];
int dp[1000][1000];
int load[100]; //存储每次的数据,调试半天发现是因为j-- 写成了j -= w[i]了,最主要的还是将两次的输入顺序写错了,是先输入all w[i]
int main()
{
int n, c;
int cnt = 0;
while (1)
{
cin >> n >> c;
if (c == 0 && n == 0)
{
break;
}
for (int i = 1; i <= n; i++)
{
cin >> w[i];
}
for (int i = 1; i <= n; i++)
{
cin >> p[i];
}
//dp
for (int i = 1; i <= n; i++)
{
for (int j = c; j >= 0; j --)
{
if (j >= w[i])
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + p[i]);
}
else
dp[i][j] = dp[i - 1][j];
}
}
load[cnt++] = dp[n][c];
}
for (int i = 0; i < cnt; i++)
{
cout << load[i] << endl;
}
}
No.4 加一乘二平方
给定两个正整数m、n,问只能做加1、乘2和平方这三种变化,从m变化到n最少需要几次
输入
输入两个10000以内的正整数m和n,且m小于n
输出
输出从m变化到n的最少次数
输入样例
1 16
输出样例
3
这个题目是bfs的模板题目,这道题是非常简单的,但是这里我还是有一个小细节错了,首先数组要设置为10001大小,之后就是在判断条件时,如果时false,那么就是 ||而不是&&,所以又运行时错误了
#include<iostream>
#include<queue>
using namespace std;
queue<int> q;
int used[10001];
int step[10001];
int start, target;
void init()
{
used[start] = 1;
step[start] = 0;
q.push(start);
}
int moveto(int u, int dire)
{
int v = 0;
if (dire == 0)
v = u + 1;
else if (dire == 1)
v = u * 2;
else
v = u * u;
return v;
}
bool canMove(int u, int dire)
{
int v = moveto(u, dire);
if (v <= target && used[v] == 0)
return true;
return false;
}
int bfs()
{
int u, v;
while (!q.empty())
{
u = q.front();
q.pop();
for (int i = 0; i < 3; i++)
{
if (canMove(u, i))
{
v = moveto(u, i);
if (v == target)
{
return (step[u] + 1);
}
used[v] = 1;
step[v] = step[u] + 1;
q.push(v);
}
}
}
}
int main()
{
cin >> start >> target;
init();
cout << bfs() << endl;
}
No.5 最大连续子序列问题 ----- noj1576
给定一整数序列A0,A1, A2,… An-1 (可能有负数),求A0An-1的一个连续子序列AiAj,使得Ai到Aj的和最大。
输入
先输入一个正整数n(1<n<1000),再输入n个整数。
输出
输出最大连续子序列的和。
输入样例
10
2 -1 5 -7 2 -1 4 -2 4 -5输出样例
7
理论考试就考到这道题目了,我太难了,当时忘记看了,加之后面的dp花了很多时间,哎;这个题目如此简单;就两种情况:第一种情况,如果前面的序列和为正,就加上当前的数字,否则就不再加了,就直接等于当前的数字了
也就是 dp(i - 1) > 0 , dp(i) = dp(i -1)(j) ;否则 dp(i) = arr(j)
#include<iostream>
using namespace std;
int num;
int arr[10001];
int dp[10001];
int main()
{
int ans = 0;
cin >> num;
for (int i = 1; i <= num; i++) //这种的都要从1开始
{
cin >> arr[i];
}
for (int i = 1; i <= num; i++)
{
if (dp[i - 1] > 0) //如果前一个的和大于0选择开始
{
dp[i] = dp[i - 1] + arr[i];
}
else
dp[i] = arr[i]; //否则从当前开始,所以就是dp[i] = a[i]
ans = max(ans, dp[i]);
}
cout << ans << endl;
}
No.6 最长公共子序列 — noj1041
描述
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=<x1, x2,…, xm>,则另一序列Z=<z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列 <i1, i2,…, ik>,使得对于所有j=1,2,…,k有:
Xij = Zj
如果一个序列S即是A的子序列又是B的子序列,则称S是A、B的公共子序列。
求A、B所有公共子序列中最长的序列的长度。输入
输入共两行,每行一个由字母和数字组成的字符串,代表序列A、B。A、B的长度不超过200个字符。
输出
一个整数,表示最长各个子序列的长度。
格式:printf("%d\n");输入样例
programming
contest输出样例
2
这道题和上面的题一样,看着挺难,但是确实很简单,代码非常短,还是使用c++,可以使用size函数,递推一般要从1开始,所以这里也都是从1开始的,这里时两个的最大公共序列,变化的量就是两个字符串的位置,所以就是二维dp,直接使用dp(i)(j)
如果a(i - 1) == b(j - 1) 那么就加上一个数,因为这里代表的时开集,如果不相等,那么就是其中一个字符串减去一个数字之后的最大值。max(dp(i)(j-1),dp(i -1)(j))
#include<iostream>
#include<string>
using namespace std;
string a, b;
int dp[10001][1001];
int main()
{
cin >> a >> b;
for (int i = 1; i <= a.size(); i++)
{
for (int j = 1; j <= b.size(); j++)
{
if (a[i - 1] == b[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
}
}
cout << dp[a.size()][b.size()] << endl;
}
No.7 电子老鼠创迷宫
描述
有一只电子老鼠被困在如下图所示的迷宫中。这是一个12*12单元的正方形迷宫,黑色部分表示建筑物,白色部分是路。电子老鼠可以在路上向上、下、左、右行走,每一步走一个格子。现给定一个起点S和一个终点T,求出电子老鼠最少要几步从起点走到终点。
(img-XtvG73rU-1636209275700)(http://noj.cn/images/problemimages/mouse.bmp)]输入
本题包含一个测例。在测例的第一行有四个由空格分隔的整数,分别表示起点的坐标S(x.y)和终点的坐标T(x,y)。从第二行开始的12行中,每行有12个字符,描述迷宫的情况,其中’X’表示建筑物,’.'表示路.
输出
输出一个整数,即电子老鼠走出迷宫至少需要的步数。
输入样例
2 9 11 8
XXXXXXXXXXXX
X…X.XXX
X.X.XX…X
X.X.XX.XXX.X
X.X…X…X
X.XXXXXXXXXX
X…X.X…X
X.XXX…XXXX
X…X…X
XXX.XXXX.X.X
XXXXXXX…XXX
XXXXXXXXXXXX输出样例
28
呃呃呃,这个题目时非常普通的广搜题目,简直太无脑了,直接写,就按照模板写下来,都没有debug就过了,只是需要注意这里都是从1开始的,并且需要判断位置处部位X
#include<iostream>
#include<queue>
using namespace std;
struct node {
int x;
int y;
};
node start,target; //起点和终点
queue<node> q;
int step[20][20];
int used[20][20];
char migong[20][20];
int mover[4] = { -1,0,+1,0 };//上左下右
int movec[4] = { 0,-1,0,+1 };
void init()
{
cin >> start.x >> start.y >> target.x >> target.y;
cin.get(); //吃掉回车
for (int i = 1; i <= 12; i++)
{
for (int j = 1; j <= 12; j++)
{
cin >> migong[i][j];
}
cin.get();
}
used[start.x][start.y] = 1;
step[start.x][start.y] = 0;
q.push(start);
}
node moveto(node u, int dire)
{
node v;
v.x = u.x + mover[dire];
v.y = u.y + movec[dire];
return v;
}
bool canMove(node u, int dire)
{
node v;
v = moveto(u, dire);
if (v.x > 0 && v.x <= 12 && v.y > 0 && v.y <= 12 && used[v.x][v.y] == 0 && migong[v.x][v.y] == '.')
return true;
return false;
}
int bfs()
{
node u, v;
while (!q.empty())
{
u = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
if (canMove(u, i))
{
v = moveto(u, i);
if (v.x == target.x && v.y == target.y)
return step[u.x][u.y] + 1;
used[v.x][v.y] = 1;
step[v.x][v.y] = step[u.x][u.y] + 1;
q.push(v);
}
}
}
}
int main()
{
init();
cout << bfs() << endl; //广搜的题果然最简单的......
}
No.8 八皇后 — noj1007
输出8皇后问题所有结果。
输入
没有输入。
输出
每个结果第一行是No n:的形式,n表示输出的是第几个结果;下面8行,每行8个字符,‘A’表示皇后,‘.’表示空格。不同的结果中,先输出第一个皇后位置靠前的结果;第一个皇后位置相同,先输出第二个皇后位置靠前的结果;依次类推。
输入样例
输出样例
输出的前几行:
No 1:
A…
…A…
…A
…A…
…A…
…A.
.A…
…A…
No 2:
A…
…A…
…A
…A…
…A.
…A…
.A…
…A…
这道题真的很简单,主要是格式太🐕了,必须是“No 2:’'
#include<iostream>
#include<math.h>
using namespace std;
int chess[10]; //八皇后棋盘
int cnt = 0;
bool canMove(int row, int col)//第row行可以放置在哪个位置
{
for (int j = 0; j < row; j++)
{
if (chess[j] == col || abs(row - j) == abs(col - chess[j]))
return false;
}
return true;
}
void output()
{
cout << "No " << cnt <<":"<< endl;
for (int i = 0; i < 8; i++)
{
for (int j = 1; j <= 8; j++)
{
if (j == chess[i])
{
cout << 'A';
}
else
cout << '.';
}
cout << endl;
}
}
void dfs(int row) //八皇后变化的是什么 :
{
if (row == 8)
{
cnt++;
output();
return;
}
for (int col = 1; col <= 8; col++)
{
if (canMove(row, col))
{
chess[row] = col; //该行记录
dfs(row + 1);
chess[row] = 0; //防止污染
}
}
}
int main()
{
dfs(0);
}
No.9 活动安排 — noj1205
活动安排----贪心算法
1205.活动安排
时限:1000ms 内存限制:10000K 总时限:3000ms描述
Jack是一名大一新生,对学校举办的各种活动都十分的好奇,想尽可能多的参加这些活动。每天共有N项活动,其开始结束时间分别为B[i],E[i],(i = 1,2,……N)
请问Jack一天最多能参加几项活动。当然,Jack在同一时间内只能参加一项活动,即jack参加的活动时间上输入
第一行 一个整数N(1<=n<=1000)表示活动总数。
接下来N行表示各活动的起始,结束时间0<=B[i]<E[i]<24输出
一个整数表示Jack最多能参加的活动数目。
输入样例
4
10 11
2 3
8 10
0 2输出样例
4
本题需要注意的问题就是活动的开始时间可以和上一项的结束时间相等,所以有一个等号,还有就是要注意将每次的结束时间进行更新
#include<iostream>
using namespace std;
struct node {
int begin;
int end;
};
node act[1001];
int main()
{
int n;
int curend;
int cnt = 1; //选择第一项
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> act[i].begin >> act[i].end;
}
//sort
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (act[j].end > act[j + 1].end)
{
node temp = act[j];
act[j] = act[j + 1];
act[j + 1] = temp;
}
}
}
//choose
curend = act[0].end;
for (int i = 1; i < n; i++)
{
if (act[i].begin >= curend)//可以相等
{
cnt++;
curend = act[i].end;
}
}
cout << cnt << endl;
}