NENU OJ算法2例题
算法2搜索E
1281: E001 数的划分
题目描述
将整数n分成k份,且每份不能为空,任意两种分法不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。
输入
每组数据由一行上的2个整数n,k构成(6<n≤200,2≤k≤6)。
输出
对每组测试数据,输出不同的分法整数。
样例输入
7 3
样例输出
4
AC代码
#include<bits/stdc++.h>
#define AUTHOR "DODOLA"
using namespace std;
typedef long long ll;
const int maxn = 220;
ll dp[maxn][maxn]; // i个小球放入j个盒子没有空盒的方法数
int main() {
int n, k;
cin >> n >> k;
for (int i = 1;i <= n;i++) {
dp[i][1] = 1;dp[i][0] = 1;
}
for (int i = 2;i <= n;i++) {
for (int j = 2;j <= k;j++) {
if (i > j)
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
else
dp[i][j] = dp[i - 1][j - 1];
}
}
cout << dp[n][k] << endl;
return 0;
}
1282: E002 闪避湖泊
题目描述
农夫约翰的农场在最近的一场暴风雨中被水淹没。但保险公司仅根据他得农场中最大的“湖泊”的大小赔偿一个数额。
农场可表示为N行M列的长方形网格,(1≤N≤100,1≤M≤100)。网格中的每个单元或是干的或是被淹没的,且恰有K个单元被水淹没,(1≤K≤N*M)。正如人们所希望的,湖泊是一个中间单元,它与其他的单元共享一条长边(不是角落)。任何与中间单元共享一条长边或者与连通单元共享一条长边的单元是一个连通单元,是湖泊的一部分。
输入
有多组数据。每组的第1行有3个整数N,M和K。第2行到第K+1行,是整数R和C,表示被淹没的位置。
输出
对每组测试数据,输出有最大湖泊的单元的数目。
样例输入
3 4 5
3 2
2 2
3 1
2 3
1 1
样例输出
4
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 150;
int n, m, k;
bool fd[maxn][maxn];
int mv[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
int ans;
int dfs(int r, int c) {
if (r<1 || r>n || c<1 || c>m || !fd[r][c])return 0;
fd[r][c] = false;
int ret = 1;
for (int i = 0;i < 4;i++) {
ret += dfs(r + mv[i][0], c + mv[i][1]);
}
return ret;
}
void solve() {
fill(fd[0], fd[0] + maxn * maxn, false);
ans = 0;
for (int ki = 0;ki < k;ki++) {
int r, c;cin >> r >> c;
fd[r][c] = true;
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
if (fd[i][j]) {
ans = max(ans, dfs(i, j));
}
}
}
cout << ans << "\n";
}
int main() {
while (cin >> n >> m >> k)
solve();
return 0;
}
/*
0 1 0 0 0
0 0 1 1 0
0 1 1 0 0
*/
1283: E003 信道分配
题目描述
当无线电台在一个非常大的区域上传播信号时,为了每个接收器都能得到较强信号,使用转发器转发信号。然而,需要仔细地选择每个转发器使用的频道,以使附近的转发器不彼此干扰。如果邻近的转发器使用不同的频道,条件就得到满足。
因为无线电波的频谱是宝贵的资源,转发器所需频道的数量应减到最少。编程任务:读取转发器网络的描述信息,并计算出所需频道的最小使用量。
输入
输入包含许多转发器网络图。每幅图的第一行是转发器数目(1~26)。转发器用连续的大写字母表示,从A开始。例如,10个转发器的名称分别是A,B,C,…,I和J。当转发器的个数是0时,表示输入结束。
转发器数目之后,是其邻近关系的列表。每行的格式为
A:BCDH
表示转发器B、C、D和H与转发器A邻近。第一行描述与转发器A邻近的,第二行描述与B邻近的,直到描述完所有的转发器。如果某个转发器不与其他转发器相邻,它的形式为
A:
转发器依字母顺序列出。
注意:相邻是对称的关系;如果A与B相邻,那么B与A也相邻。因为转发器位于水平面内,由相邻的转发器构成的网络图没有相交的线。
输出
对于每幅图(除了最后一个没有转发器),输出一行,是转发器不互相干扰所需的最少频道数。输出格式参考样例输出。注意:频道数为1的话,“channel”为单数。
样例输入
2
A:
B:
4
A:BC
B:ACD
C:ABD
D:BC
0
样例输出
1 channel needed.
3 channels needed.
AC代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 120;
bool fd[maxn][maxn];
int ind[30], t;
bool dfs(int from, int clr) {
// 从from着色
for (int i = 0;i < clr;i++) {
bool f = true;ind[from] = i;
for (int j = 0;j < from;j++) {
if (ind[j] == i && fd[from][j]) {
f = false;
break;
}
}
if (f && (from == t - 1 || dfs(from + 1, clr)))
return true;
}
return false;
}
int main() {
while (cin >> t) {
cin.get();
if (t == 0)break;
memset(fd, 0, sizeof(fd));
memset(ind, 0, sizeof(ind));
bool f = true;
for (int i = 0;i < t;i++) {
string msg;cin >> msg;
if (msg.size() == 2)
continue;
f = false;
int pid = msg[0] - 'A';
for (int j = 2;j < msg.size();j++) {
fd[pid][msg[j] - 'A'] = true;
fd[msg[j] - 'A'][pid] = true;
}
}
if (f)
cout << "1 channel needed.\n";
else if (dfs(1, 2))
cout << "2 channels needed.\n";
else if (dfs(1, 3))
cout << "3 channels needed.\n";
else
cout << "4 channels needed.\n";
}
return 0;
}
1284: E004 移动的骑士
题目描述
你的一个朋友正在研究骑士旅行问题(TKP)。在一个有n个方格的棋盘上,你得找到一条最短的封闭骑士旅行的路径,使能够遍历每个方格一次。他认为问题的最困难部分在于,对两个给定的方格,确定骑士移动所需的最小步数。所以你帮助他编写一个程序,解决这个“困难的”部分。你的任务是:输入有两个方格a和b,确定骑士在最短路径上从a到b移动的次数。
国际象棋中的骑士在棋盘上可移动的范围如下图:
输入
输入包含一组或多组测试例。每个测试例一行,是两个方格,用空格隔开。棋盘上的一个方格用一个字符串表示,字母(a-h)表示列,数字(1-8)表示行。
输出
对每个测试例,输出一行:“To get from xx to yy takes n knight moves.”。
样例输入
e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6
样例输出
To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 150;
string fs, ts;
int mv[8][2] = { {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1} };
void bfs(int x1, int y1, int x2, int y2) {
if (x1 == x2 && y1 == y2) {
cout << "To get from " << fs << " to " << ts << " takes 0 knight moves.\n";
return;
}
int stp = 0;
queue<pair<int, int>> q;
q.push({ x1, y1 });
while (!q.empty()) {
queue<pair<int, int>> qs;
while (!q.empty()) {
auto [x, y] = q.front();q.pop();
for (int i = 0;i < 8;i++) {
int nx = x + mv[i][0], ny = y + mv[i][1];
if (nx < 0 || nx >= 8 || ny < 0 || ny >= 8)continue;
if (nx == x2 && ny == y2) {
cout << "To get from " << fs << " to " << ts << " takes " << stp + 1 << " knight moves.\n";
return;
}
qs.push({ nx, ny });
}
}
stp++;
q = qs;
}
}
void solve() {
int x1 = fs[0] - 'a', y1 = fs[1] - '1', x2 = ts[0] - 'a', y2 = ts[1] - '1';
bfs(x1, y1, x2, y2);
}
int main() {
while (cin >> fs >> ts)
solve();
return 0;
}
1285: E005 图像周长
题目描述
病理学实验室的技术人员需要分析幻灯片的数字图像。幻灯片上有许多要分析的目标,由鼠标单击确定一个目标。目标边界的周长是一个有用的测量参数。编程任务:确定选中目标的在周长。
数字化的幻灯片是一个矩形的网格,里面有点’.’,表示空的地方;有大写字母‘X’,表示目标的一部分。简单网格如下所示
方格中的一个X是指一个完整的网络方形区域,包括其边界和目标本身。网格中心的X与其边界上8个方向的X都是相邻的。任何两个相邻的X,其网格方形区域在边界或者拐角处是重叠的,所以他们的网格方形区域是相邻的。
一个目标是由一系列相邻X的网格方形区域连接起来构成的。在网格1中,一个目标填充了全部网格;在网格2中有两个目标,其中一个目标只占左下角的一个网格方形区域,其余的X属于另一个目标。
技术人员总是能单击到一个X,以选中包含该X的目标,记录单击时的坐标。行列号是从左上角开始,从1开始编号的。在网格1中,技术人员可以单击行2和列2选择目标;在网格2中,单击行2和列3就可以选中较大目标,单击行4和列3就不能选中任何目标。
一个有用的统计参数是目标的周长。假定每个X的每条边上有一个方形的单元。在网格1中目标的周长是8(4个边,每个边上有2个方形的单元);在网格2中,较大目标的周长是18,如下图所示。
目标中不会包含任何完全封闭的孔,所以下面最左边的网格不会出现,应该是右边的网格样式。
输入
输入有多组网格。对每个网格,第一行是网格的行列数(rows,columns),鼠标单击的行列号(row,column),其整数范围都是1-20.接下来就是rows行,由字符‘.’和‘X’构成。
当一行是4个0时,标志输入结束。一行中的4个数字之间各有一个空格。网格数据的行之间没有空行。
输出
对每个网络输出一行,是选中目标的周长。
样例输入
2 2 2 2
XX
XX
6 4 2 3
.XXX
.XXX
.XXX
...X
..X.
X...
5 6 1 3
.XXXX.
X....X
..XX.X
.X...X
..XXX.
0 0 0 0
样例输出
8
18
40
AC代码
#include<bits/stdc++.h>
#define AUTHOR "DODOLA"
// #include<queue>
// #include<iostream>
// #include<string>
using namespace std;
const int maxn = 250;
typedef long long ll;
int r, c, x, y;
vector<string>mp(maxn);
int mv[8][2] = {
{1,0},{0,1},{-1,0},{0,-1},
{1,1},{1,-1},{-1,-1},{-1,1},
};
int ans;
bool ck[maxn][maxn];
void dfs(int px, int py) {
// cout << px << " " << py << endl;
// cout << ans << endl;
if (mp[px][py] != 'X' || ck[px][py])return;
ck[px][py] = true;
for (int j = 0;j < 4;j++) {
int xj = px + mv[j][0], yj = py + mv[j][1];
if (mp[xj][yj] != 'X')
ans++;
}
for (int i = 0;i < 8;i++) {
int xi = px + mv[i][0];
int yi = py + mv[i][1];
dfs(xi, yi);
}
}
string s0('.', maxn - 1);
void solve() {
ans = 0;
fill(ck[0], ck[0] + sizeof(ck), false);
fill(mp.begin(), mp.end(), s0);
for (int i = 1;i <= r;i++) {
cin >> mp[i];
mp[i] = " " + mp[i] + " ";
}
dfs(x, y);
cout << ans << "\n";
}
int main() {
mp[0] = s0;
while (cin >> r >> c >> x >> y) {
if (!r && !c && !x && !y)break;
solve();
}
return 0;
}
1286: E006 移动的骑士
题目描述
Somurolov先生是一个国际象棋高手,他声称在棋盘上将骑士棋子从一点移动到另外一点,没有人比他快,你敢挑战他吗?
你的任务是编程计算出将一个骑士棋子从一点移动到另外一点,最少需要移动的步数。显而易见,这样你就有赢得Somurolov先生的机会。国际象棋中的骑士在棋盘上可移动的范围如下图:
输入
首先输入测试样例的个数n。接下来是n组输入数据,每组测试数据由三行整数组成:第一行是棋盘的边长l (4 <= l <= 300),整个棋盘的面积也就是 ll;第二行和第三行分别是骑士棋子的初始位置和目标位置,表示为整数对形式{0, …, l-1}{0, …, l-1}。保证棋子的初始和目标位置是棋盘上的合法位置。
输出
对于每一个输入的测试样例,请你算出骑士从初始位置移动到目标位置最小移动步数。如果初始位置和目标位置相同,那么骑士移动的距离就是0。最后单独一行输出所求距离。
样例输入
1
8 0 0
7 0
样例输出
5
AC代码
#include <iostream>
#include <queue>
#include <cstring>
#define AUTHOR "DODOLA"
using namespace std;
struct P {
int x;
int y;
int step;
};
int mv[8][2] = {
{1, 2}, {2, 1}, {-1, 2}, {-2, 1},
{1, -2}, {2, -1}, {-1, -2}, {-2, -1}
};
int bfs(int n, int x1, int y1, int x2, int y2)
{
if (x1 == x2 && y1 == y2)
{
return 0;
}
bool gnd[n][n];
queue<P> q;
memset(gnd, 0, sizeof(gnd));
P start, node;
start.x = x1;
start.y = y1;
start.step = 0;
q.push(start);
while (!q.empty()) {
int x0, y0, step;
start = q.front();
q.pop();
x0 = start.x;
y0 = start.y;
step = start.step;
for (int j = 0; j < 8; j++) {
int x3 = x0 + mv[j][0];
int y3 = y0 + mv[j][1];
if (x3 == x2 && y3 == y2)
return step + 1;
if (x3 >= 0 && x3 < n && y3 >= 0 && y3 < n && !gnd[x3][y3]) {
node.x = x3;
node.y = y3;
node.step = step + 1;
q.push(node);
gnd[x3][y3] = 1;
}
}
}
return 0;
}
int main() {
int t;
cin >> t;
while (t--) {
int x1, y1, x2, y2, n;
cin >> n >> x1 >> y1 >> x2 >> y2;
cout << bfs(n, x1, y1, x2, y2) << endl;
}
return 0;
}
1287: E007 寻找M
题目描述
给出一个整数n,编程求出一个非零整数m,使得m是n的倍数,并且m的十进制表示中只有1和0。给出的n不大于200并且肯定存在对应的m,m是十进制数并且不大于100位。
输入
输入包含多组测试数据。每组测试数据只有一个整数n (1 <= n <= 200)。整数0标志输入的结束。
输出
对于每个n输出对应的整数m,m的十进制表示不多于100位。如果对于一个n存在多个合法的m,你只需输出一个即可。
样例输入
2
6
0
样例输出
10
1110
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10;
int n;
bool ck(int x) {
while (x) {
if (x % 10 == 0 || x % 10 == 1) {
x /= 10;
continue;
}
else return false;
}
return true;
}
void solve() {
for (int i = 1;;i++) {
int m = i * n;
bool f = ck(m);
if (f) {
cout << m << endl;
return;
}
}
}
int main() {
while (cin >> n) {
if (n == 0)break;
solve();
}
return 0;
}
1288: E008 红与黑
题目描述
有一个矩形的房间,房间铺着正方形的地砖。每个地砖被涂上红色或者黑色。初始时你站在房间里的某个黑色地砖上,你每次只能移动到相邻的四个地砖之一,即上下左右移动,并且你每次只能移动到黑色的地砖上,不能走到红色地砖。
编程计算出按照上述要求你能走到的黑色地砖的个数。
输入
输入包含多组测试数据。每组测试数据第一行包括2个整数W和H;W和H是房间的宽度和长度,分别表示为房间的x和y坐标轴。W和H不大于20。接下来是H行每行W个地砖的房间,每个地砖表示如下:
‘.’——黑色地砖
‘#’——红色地砖
‘@’ ——你在房间里的初始位置(房间只出现一次)。
输入的最后一行是两个整数0,不用处理。
输出
对每个测试样例,输出一行,即你能走到的黑色地砖的个数(包括你初始站在的黑色地砖)。
样例输入
6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
0 0
样例输出
45
59
AC代码
#include <iostream>
#include <queue>
#include <cstring>
const int maxn = 120;
using namespace std;
int r, c;
char mp[maxn][maxn];
int mv[4][2] = {
{0,1},{0,-1},{1,0},{-1,0},
};
int ans;
void dfs(int x, int y) {
if (x < 0 || x == c || y < 0 || y == r)return;
if (mp[x][y] == '#')return;
ans++;
mp[x][y] = '#';
for (int i = 0;i < 4;i++) {
int xi = x + mv[i][0];
int yi = y + mv[i][1];
dfs(xi, yi);
}
}
void solve() {
ans = 0;
int px, py;
for (int i = 0;i < c;i++) {
for (int j = 0;j < r;j++) {
cin >> mp[i][j];
if (mp[i][j] == '@') {
px = i;py = j;
}
}
}
dfs(px, py);
cout << ans << endl;
}
int main() {
while (cin >> r >> c) {
if (!r && !c)break;
solve();
}
return 0;
}
1289: E009 小木棒
题目描述
George有一些长度相等的木棒,他随意的将这些木棒切成长度最多是50的小木棒。麻烦来了,他现在想将这些杂乱的小木棒恢复到原来的木棒,但是他忘记了原来到木棒的数量和长度。请你帮助他设计一个程序计算出原来木棒可能的最小长度,所有小木棒的长度均表示为大于0的整数。
输入
每组输入数据包括两行。第一行是George切后小木棒的个数,最多有64根小木棒;第二行是这些小木棒的长度,这些长度表示为空格分开的整数。输入样例以整数0表示结束。
输出
输出一行,即为原始木棒可能的最小长度。
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
6
5
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 80;
int n, a[maxn], maxv, tot, vis[maxn], k, len;
bool cmp(int a, int b) {
return a > b;
}
bool dfs(int i, int rest, int p) {
if (i > k)
return 1;
int fail = 0;
for (int x = p + 1;x <= n;x++) {
if (!vis[x]) {
if (a[x] == a[fail]) continue;
if (rest > a[x]) {
vis[x] = 1;
bool w = dfs(i, rest - a[x], x);
vis[x] = 0;
if (!w) fail = x;
if (w) return 1;
}
else if (rest == a[x]) {
vis[x] = 1;
bool w = dfs(i + 1, len, 0);
vis[x] = 0;
return w;
}
if (p == 0) return 0;
}
}
return 0;
}
void solve() {
fill(vis, vis + n + 5, 0);
maxv = tot = 0;
for (int i = 1;i <= n;i++) {
cin >> a[i];
maxv = max(maxv, a[i]);
tot += a[i];
}
sort(a + 1, a + 1 + n, cmp);
for (len = maxv;len <= tot;len++) {
if (tot % len == 0) {
k = tot / len;
if (dfs(1, len, 0)) {
cout << len << endl;
break;
}
}
}
}
int main() {
while (cin >> n && n)
solve();
return 0;
}