bfs & dfs
题目链接:https://vjudge.net/contest/404511
1.最短路(bfs)
(1)一维最短路
D - Catch That Cow
题目大意:
在一条数轴上,奶牛和农夫分别在两个点,农夫有三种移动方式,
方式1:x -> x + 1; 方式2: x -> x - 1; 方式3 :x -> 2x。
每次移动需要1分钟,假定奶牛不会动,求农夫找到奶牛最少需要几分钟。
**分析:**用bfs,队列实现,可以找到最短路,因为队列取队首,每次都会取出队列中用时最短的那个,然后移动一次后在push进去,简单说就是先处理0分钟的,然后push进去,再处理1分钟的,push进去,再处理2分钟的。。。。。这样的话只要找到奶牛了就一定是最短的路。
注意:
1.定义结构体,保存当前位置,以及走到这里用了多长时间。
2.队列,vis的清空。
3.判断是否越界,是否来过(根据上面的分析可知,如果某点已经来过,那现在这次来这个点一定比第一次来的时候用时长)
4.这个题数据范围给的是小于100000,但是有种移动方式是*2,所以数组按照以前多开5个10个的会re,最好开200000以上。
AC 代码
#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
struct cow
{
int x;//当前步数
int sum;//走到这里用了多长时间
};
bool vis[200000];//标记是否来过,*2,开大点
int n, k, ans = 0;
queue<cow> q;
void bfs(int n,int k)
{
cow u, v;
//结构体初始化
u.x = n;
u.sum = 0;
vis[u.x] = 1;
q.push(u);
while(!q.empty())
{
u = q.front();
q.pop();
v = u;
if(k == v.x)
{
ans = v.sum;
return ;
}
//三种移动方式
if(!vis[v.x * 2] && v.x + v.x <= 100000 )
{
v.x = v.x + v.x;
v.sum += 1;
q.push(v);
vis[v.x] = 1;
}
v = u;
if(!vis[v.x + 1] && v.x + 1 <= 100000 )
{
v.x += 1;
v.sum += 1;
q.push(v);
vis[v.x] = 1;
}
v = u;
if(!vis[v.x - 1] && v.x - 1 >= 0)
{
v.x -= 1;
v.sum += 1;
q.push(v);
vis[v.x] = 1;
}
}
}
int main()
{
while(~scanf("%d %d", &n, &k))
{
//队列,vis的清空,另外,队列可以不定义在全局,定义在bfs里这样也不需要清空
while(!q.empty())
{
q.pop();
}
memset(vis, 0, sizeof(vis));
bfs(n, k);
printf("%d\n",ans);
}
return 0;
}
(2)三维最短路
C - Dungeon Master
题目大意:
你被困在一个三维地牢里,当前位置是s,出口在e,#是墙,.是路,你可以香南北东西上下6个方向移动,每层输入结束后,与下一层之间有一个空行,输出逃出地牢最少用时,如不能逃出,输出Trapped!
分析:
跟上一个题基本一个题。
注意
1.三维x,y,z三个坐标对应好了
2.输入的时候用%s一行一行输,用%c的话好像会吃回车
3.从0开始还是从1开始
4.行和列,x和y
5.判断的时候,注意.和E都是可以走的,好像有一次写的是只有.可以走导致一直没输出正确结果,其实这要判断那个不能走,不管可以走的话就可以避免这个问题。
剩下的和那个抓牛的题差不多
AC代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
char ch[34][34][34];
bool vis[34][34][34] = {0};
int L, R, C, ans;
//6个移动方向
int dx[6] = {1, 0, -1, 0, 0, 0};
int dy[6] = {0, -1, 0, 1, 0, 0};
int dz[6] = {0, 0, 0, 0, 1, -1};
int x1, y1, z1;//用来储存起点
struct node
{
int x, y, z;
int step;
};
bool judge(int x, int y, int z)
{
if(x < 1 || y < 1 || z < 1 || x > C || y > R || z > L)//越界
return false;
else if(ch[z][y][x] == '#')//墙
return false;
else if(vis[z][y][x])//走过
return false;
else
return true;
}
int bfs()
{
node now, next;//一个结构体是当前位置,一个是下一个位置,英语好的都能看懂哈哈
queue<node> q;
now.x = x1;//初识化,搜索的起点,各个点坐标,步数,标记,push
now.y = y1;
now.z = z1;
now.step = 0;
vis[now.z][now.y][now.x] = 1;
q.push(now);
while(!q.empty())
{
now = q.front();//取队首,并pop,然后判断是否到终点
q.pop();
if(ch[now.z][now.y][now.x] == 'E')
{
ans = now.step;
return ans;
}
for(int i = 0; i < 6; i++)//没到的话,6个方向
{
next.x = now.x + dx[i];
next.y = now.y + dy[i];
next.z = now.z + dz[i];
if(!judge(next.x,next.y,next.z))
continue;
vis[next.z][next.y][next.x] = 1;
next.step = now.step + 1;
q.push(next);
}
}
return 0;
}
int main()
{
while(~scanf("%d %d %d", &L, &R, &C) && L + R + C)
{
memset(vis,0,sizeof(vis));
for(int i = 1; i <= L; i++)
{
for(int j = 1; j <= R; j++)
{
scanf("%s",ch[i][j] + 1);
for(int k = 1; k <= C; k++)
{
if(ch[i][j][k] == 'S')
{
x1 = k;
y1 = j;
z1 = i;
}
}
}
}
ans = bfs();
if(ans)
printf("Escaped in %d minute(s).\n",ans);
else
printf("Trapped!\n");
}
return 0;
}
G - 胜利大逃亡
分析:
跟上个题一样,就是多了一个比较
注意:
1.有可能这个地图是根本走不出来的,这样的情况要输出-1;
2.ans清零
AC代码
/*********每步代码的含义注释里没写,因为跟C题差不多********/
//下面的那几道最短路也是,差不多的
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
struct node
{
int x, y, z;
int step;
};
bool ar[55][55][55];
bool vis[55][55][55] = {0};
int n, a, b, c, t, ans;
int dx[6] = {1, 0, -1, 0, 0, 0};
int dy[6] = {0, -1, 0, 1, 0, 0};
int dz[6] = {0, 0, 0, 0, 1, -1};
bool judge(int x, int y, int z)
{
if( x < 0 || y < 0 || z < 0 || x >= c || y >= b || z >= a)
return true;
else if(ar[z][y][x])
return true;
else if(vis[z][y][x])
return true;
else
return false;
}
void bfs()
{
memset(vis, 0, sizeof(vis));
node now, next;
queue<node> q;
now.x = 0;
now.y = 0;
now.z = 0;
now.step = 0;
vis[now.z][now.y][now.x] = 1;
q.push(now);
while(!q.empty())
{
now = q.front();
q.pop();
if(now.z == a - 1 && now.y == b - 1 && now.x == c - 1)
{
ans = now.step;
return ;
}
for(int i = 0; i < 6; i++)
{
next.z = now.z + dz[i];
next.y = now.y + dy[i];
next.x = now.x + dx[i];
if(judge(next.x,next.y,next.z))
continue;
vis[next.z][next.y][next.x] = 1;
next.step = now.step + 1;
q.push(next);
}
}
}
int main()
{
scanf("%d",&n);
for(int m = 1; m <= n; m++)
{
scanf("%d %d %d %d",&a, &b, &c, &t);
for(int i = 0; i < a; i++)
{
for(int j = 0; j < b; j++)
{
for(int k = 0; k < c; k++)
{
scanf("%d",&ar[i][j][k]);
}
}
}
bfs();
// if(ans <= t)
// printf("%d\n",ans);
// else
// printf("-1\n");
if(ans)
{
if(ans <= t)
printf("%d\n",ans);
else
printf("-1\n");
}
else
printf("-1\n");
memset(ar, 0, sizeof(ar));
ans = 0;
}
return 0;
}
//两次对拍,第一次发现有的地图会出不来,bfs返回值为0,这种情况应该输出-1
//第二次发现ans忘记清零惹!!!!
H - A计划
分析:
还是差不多,就是注意传送门,传过去之后要立马判断,传过去之后不能是传送门和墙
AC代码
这个没写judge看着乱乱的,以后有时间改改。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct node
{
int x, y, z;
int step;
};
int c, n, m, t;
char ch[2][15][15];
bool vis[2][15][15];
int dx[] = {1, 0, 0, -1};
int dy[] = {0, 1, -1, 0};
bool bfs()
{
queue<node> q;
node now, next;
memset(vis, 0, sizeof(vis));
now.x = 0;
now.y = 0;
now.z = 0;
now.step = 0;
q.push(now);
vis[0][0][0] = 1;
while(!q.empty())
{
now = q.front();
q.pop();
if(ch[now.z][now.y][now.x] == 'P' && now.step <= t)
return 1;
for(int i = 0; i < 4; i++)
{
next.x = now.x + dx[i];
next.y = now.y + dy[i];
next.z = now.z;
next.step = now.step + 1;
if(next.x >= 0 && next.x < m && next.y >= 0 && next.y < n && !vis[next.z][next.y][next.x])
{
vis[next.z][next.y][next.x] = 1;
if(ch[next.z][next.y][next.x] == '#')
{
next.z = 1 - next.z;
if(ch[next.z][next.y][next.x] != '*' && ch[next.z][next.y][next.x] != '#' && !vis[next.z][next.y][next.x])
q.push(next);
}
else if(ch[next.z][next.y][next.x] == '.' || ch[next.z][next.y][next.x] == 'P')
q.push(next);
}
}
}
return 0;
}
int main()
{
scanf("%d",&c);
while(c--)
{
scanf("%d %d %d", &n, &m, &t);
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < n; j++)
{
scanf("%s",ch[i][j]);
}
}
if(bfs())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
2.bfs & dfs
这两道题是师哥讲的,就不多哔哔惹
A - Red and Black
题意:
先输入两个数,第一个一个代表列数n,第二个代表行数m,接下来的输入n*m的地图由 ‘.’ ‘@’ ‘#’ 构成,@是起始位置,.可以走,#不可以走,问可以走的点有几个
分析:
普通的bfs搜索,注意输入的第一个数是行,第二个数是列
AC代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
struct node
{
int x;
int y;
};
queue<node> q;
char ch[25][25];
int a, b;
int m, n;
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
bool vis[25][25];
int ans = 0;
bool judge(int x, int y)
{
if(!vis[x][y] && x > 0 && x <= n && y > 0 && y <= m && ch[x][y] == '.')
return true;
else
return false;
}
void bfs(int x, int y)
{
node u, v;
vis[x][y] = 1;
ans++;
u.x = x;
u.y = y;
q.push(u);
while(!q.empty())
{
u = q.front();
q.pop();
for(int i = 0; i < 4; i++)
{
v = u;
v.x += dx[i];
v.y += dy[i];
if(judge(v.x, v.y))
{
ans++;
q.push(v);
vis[v.x][v.y] = 1;
}
}
}
}
int main()
{
while(~scanf("%d %d",&m, &n) && n + m)
{
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
{
scanf("%s",ch[i] + 1);
}
ans = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(ch[i][j] == '@')
bfs(i,j);
}
}
printf("%d\n",ans);
}
return 0;
}
这个题也可以用dfs来做
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include <queue>
#include <stack>
using namespace std;
int n, m;
char s[22][22];
int x, y;
int ans;
int vis[22][22];
int judge(int x, int y) {
if (x < 1 || x > m || y < 1 || y > n)
return 0;
return 1;
}
void bfs(int x, int y) {
if (vis[x][y] == 1 || !judge(x, y) || s[x][y] == '#')
return;
vis[x][y] = 1;
ans++;
bfs(x + 1, y);
bfs(x - 1, y);
bfs(x, y + 1);
bfs(x, y - 1);
}
int main() {
while (scanf("%d%d",&n,&m)!=EOF) {
ans = 0;
memset(vis, 0 ,sizeof vis);
if (n == 0 && m == 0)
break;
getchar();
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
scanf("%c", &s[i][j]);
if (s[i][j] == '@') {
x = i;
y = j;
}
}
getchar();
}
bfs(x, y);
printf("%d\n",ans);
}
return 0;
}
K - Oil Deposits
题意:
找有几块大油田。
bfs AC代码
#include <bits/stdc++.h>
using namespace std;
const int M = (int)1e2;
int n, m;
char s[M + 5][M + 5];
bool vis[M + 5][M + 5];
struct node
{
int x, y;
};
queue<node> q;
int dx[] = {0, -1, -1, -1, 0, 1, 1, 1};
int dy[] = {1, 1, 0, -1, -1, -1, 0, 1};
void bfs(int x, int y)
{
node u, v;
u.x = x, u.y = y;
vis[u.x][u.y] = 1;
q.push(u);
while(!q.empty())
{
node u = q.front();
q.pop();
for(int i = 0; i < 8; ++i)
{
v = u;
v.x += dx[i], v.y += dy[i];
if(v.x < 0 || v.x > n || v.y < 0 || v.y > m) continue;
if(!vis[v.x][v.y] && s[v.x][v.y] == '@')
{
q.push(v);
vis[v.x][v.y] = 1;
}
}
}
}
int main()
{
while(~scanf("%d %d", &n, &m) && n + m)
{
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
int ans = 0;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(!vis[i][j] && s[i][j] == '@')
{
bfs(i, j);
++ans;//注意ans增加的位置
}
}
}
printf("%d\n", ans);
}
return 0;
}
dfs AC代码
#include <bits/stdc++.h>
using namespace std;
const int M = (int)1e2;
int n, m;
char s[M + 5][M + 5];
bool vis[M + 5][M + 5];
int dx[] = {0, -1, -1, -1, 0, 1, 1, 1};
int dy[] = {1, 1, 0, -1, -1, -1, 0, 1};
void dfs(int x, int y)
{
for(int i = 0; i < 8; ++i)
{
int xx = x + dx[i], yy = y + dy[i];
if(xx < 0 || xx > n || yy < 0 || yy > m) continue;
if(!vis[xx][yy] && s[xx][yy] == '@')
{
vis[xx][yy] = 1;
dfs(xx, yy);
}
}
}
int main()
{
while(~scanf("%d %d", &n, &m) && n + m)
{
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
int ans = 0;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(!vis[i][j] && s[i][j] == '@')
{
vis[i][j] = 1;
dfs(i, j);
++ans;
}
}
}
printf("%d\n", ans);
}
return 0;
}
3. dfs
B - 棋盘问题
分析:
用dfs搜索,从两个维度搜,dfs由 x = 0 搜到 x = n - 1,每个dfs里由y = 0 搜到 y = n - 1。
AC 代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
char ch[10][10];
bool vis[10];
int ans, cnt, n, k;
/*
ans有几种放法
cnt表示当前放了几枚棋子
*/
void dfs(int x)
{
if(cnt == k) //当前所放棋子等于所求,则放法+1,并return
{
ans++;
return ;
}
if(x == n) return ;//x = n,说明越界了,return
for(int i = 0; i < n ; i++)//搜y
{
if( !vis[i] && ch[x][i] == '#')
{
vis[i] = 1;
cnt++;
dfs(x + 1);//搜下一行
cnt--;//回溯之后cnt减为0,所有被标记的点清零
vis[i] = 0;
}
}
dfs(x + 1);//这一行没有的话,搜下一行
}
int main()
{
while(~scanf("%d %d", &n, &k))
{
if(n == -1 && k == -1)
break;
ans = 0;
cnt = 0;
for(int i = 0; i < n; i++)
{
scanf("%s", ch[i]);
}
dfs(0);
printf("%d\n", ans);
}
return 0;
}
J - N皇后问题
分析:
1.用dfs, dfs由 x = 0 搜到 x = n - 1,每个dfs里再由y = 0 搜到 y = n - 1(很想b题)
2.如何标记,定义一个二维数组,第一维3个数即可,0代表横行,1代表倾角45°的斜线,2代表倾角135°的斜线,
3.对于横行他们的共同的特点是y相同,倾角45°的斜线的话,他们的截距相同,但是有的直线截距为负数,为避免这个问题,我们用n - 截距即可,135°的话同45°
4.n皇后问题,且n小于10,我们先把1到10皇后问题的结果都求出来,存在数组里,再根据输入,输出对应的答案,这样好像是可以避免前台后台之前频繁的转换,否则会TLE。
AC 代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
bool vis[3][100];//注意这三维各自含义
int n, x, y;
int ans[15];//存答案
int i = 0;
void dfs(int x)
{
if(x == n)
{
ans[n]++;
}
else
{
for(int y = 0; y < n; y++)
{
if(!vis[0][y] && !vis[1][n - y + x] && !vis[2][x + y])
{
vis[0][y] = vis[1][n - y + x] = vis[2][x + y] = 1;
dfs(x + 1);
vis[0][y] = vis[1][n - y + x] = vis[2][x + y] = 0;
}
}
}
}
int main()
{
for(int i = 0; i <= 10; i++)
{
n = i;
memset(vis, 0, sizeof(vis));
dfs(0);
}
while(~scanf("%d",&n) && n)
printf("%d\n",ans[n]);
return 0;
}
L - 变形课
分析:
每个字符串只有首字母和尾字母有用,每次输入记录首尾字母放在两个数组里,输入的时候要注意,输入之后立刻进行判断,若字符串为0则开始搜索,调用dfs,否则进行首尾字母的储存与计数。
dfs的话,进来之后首先判断ans是否为1,是则返回,否则判断当前序号字符串尾字母是否为m,是则将ans赋值为1并返回,否则遍历所有字符串找出为访问且首字母与当前字符串尾字母相同的,从此开始搜索。
AC 代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n = 0;
string s;
char sta[1050], edd[1005];
bool ans, vis[1005];
void dfs(int k)
{
if(ans == 1)//已找到
return ;
if(edd[k] == 'm')
{
ans = 1;
return ;
}
for(int i = 1; i <= n; i++)//遍历寻找未访问且首尾可相连的,从此开始搜索
{
if(!vis[i] && sta[i] == edd[k])
{
vis[i] = 1;
dfs(i);
vis[i] = 0;//回溯取消标记
}
}
}
int main()
{
while(cin>>s)
{
if(s[0] == '0')//判断,找到第一个b,并从此开始搜索
{
for(int i = 1; i <= n; i++)
{
vis[i] = 0;
}
for(int i = 1; i <= n; i++)
{
if(sta[i] == 'b')
{
vis[i] = 1;
dfs(i);
}
}
if(ans)
printf("Yes.\n");
else
printf("No.\n");
n = 0;
ans = 0;
}
else//保存首尾字母
{
n++;
sta[n] = s[0];
edd[n] = s[s.size() - 1];
}
}
return 0;
}
K-A Knight’s Journey
Description
整天待在方块里的骑士感到特别的无聊,于是他决定来一场说走就走的旅行。
然而他只能走日字,如右图所示,如果骑士当前在棋盘的正中央,他可以走标记有白点的八个区域。
骑士知道世界是一个列数和行数均不超过8(即8×8)的棋盘。
并且骑士有一点强迫症,如果用A-Z来表示列,1-99来表示横行,他只愿意走字典序最小的一条道路。
你能帮助勇敢的骑士制定一个适合他的旅行计划,使得他可以走遍整个棋盘吗?骑士可以在任一方块出发或者结束。
这里写图片描述
Input
第一行中有一个正整数n,代表数据有n组。
对于每组数据,都含有两个正整数p和q(1 <= p * q <= 26),代表棋盘有p行q列。
Output
每组数据首先应当输出”Scenario #i:”,i代表输出的是第i组数据的结果。
然后在一行之内输出一条可以走遍棋盘的路径,如果有多条路径可以走遍棋盘,那么输出按字典序排序第一的路径。
最后,留一个空行。若现在是最后一条数据,则不留空行。
在输出路径时,用A代表第一列,B代表第二列…以此类推。而使用1代表第一行,2代表第二行。
例如,若要表示从第一行第一列到第二行第三列,可以用字符串:A1C3来表示。
Sample Input
样例输入①:
3
1 1
2 3
4 3
样例输入②:
4
5 5
3 3
4 5
6 3
Sample Output
样例输出①:
Scenario #1:
A1
Scenario #2:
impossible
Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4
样例输出②:
Scenario #1:
A1B3A5C4A3B1D2E4C5A4B2D1C3B5D4E2C1A2B4D5E3C2E1D3E5
Scenario #2:
impossible
Scenario #3:
A1B3C1A2B4D3E1C2D4E2C3A4B2D1E3C4A3B1D2E4
Scenario #4:
impossible
分析:
-
应该看到这个题就可以想到用DFS,当首先要明白这个题的意思是能否只走一遍(不回头不重复)将整个地图走完,而普通的深度优先搜索是一直走,走不通之后沿路返回到某处继续深搜。所以这个题要用到的回溯思想,如果不重复走一遍就走完了,做一个标记,算法停止;否则在某种DFS下走到某一步时按马跳的规则无路可走而棋盘还有为走到的点,这样我们就需要撤消这一步,进而尝试其他的路线(当然其他的路线也可能导致撤销),而所谓撤销这一步就是在递归深搜返回时重置该点,以便在当前路线走一遍行不通换另一种路线时,该点的状态是未访问过的,而不是像普通的DFS当作已经访问了。
-
如果有多种方式可以不重复走一遍的走完,需要输出按字典序最小的路径,而注意到国际象棋的棋盘是列为字母,行为数字,如果能够不回头走一遍的走完,一定会经过A1点,所以我们应该从A1开始搜索,以确保之后得到的路径字典序是最小的(也就是说如果路径不以A1开始,该路径一定不是字典序最小路径),***而且我们应该确保优先选择的方向是字典序最小的方向,即按照图片中的顺序走,先走1的方向,再走2的方向,以此类推,***这样我们最先得到的路径就是字典序最小的。
3.注意坐标,x正方向向下,y正方向向右
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int xy[100][100];//记录第几步的位置,第一维表示步数,第二维只用1和2表示x,y
bool vis[100][100];
int n, p, q;
bool flag;
//字典序最小的行走方向
int dx[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
int dy[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
bool judge(int x, int y)
{
if(x >= 1 && x <= p && y >= 1 && y <= q && !vis[x][y] && !flag)
return true;
return false;
}
void dfs(int a, int b, int step)
{
//记录第step步时的位置
xy[step][1] = a;
xy[step][2] = b;
if(step == p * q)//已走步数等于格数,即已经走完
{
flag = true;
return ;
}
for(int i = 0; i < 8; i++)
{
int next_x = a + dx[i];
int next_y = b + dy[i];
if(judge(next_x, next_y))
{
vis[next_x][next_y] = 1;
dfs(next_x, next_y, step + 1);
vis[next_x][next_y] = 0;//撤销该步
}
}
}
int main()
{
scanf("%d", &n);
for(int m = 1; m <= n ; m++)
{
flag = 0;
scanf("%d %d", &p, &q);
memset(vis, 0,sizeof(vis));
vis[1][1] = 1;
dfs(1, 1, 1);
printf("Scenario #%d:\n", m);
if(flag)
{
for(int i = 1; i <= p * q; i++)
printf("%c%d",xy[i][2] - 1 + 'A', xy[i][1]);//int转char
}
else
printf("impossible");
printf("\n");
if(m != n)
printf("\n");
}
return 0;
}
I - Sum It Up (数 与 和 )
Given a specified total t and a list of n integers, find all distinct sums using numbers from the list that add up to t. For example, if t=4, n=6, and the list is 4,3,2,2,1,14,3,2,2,1,1, then there are four different sums that equal 4: 4,3+1,2+2, and 2+1+1.(A number can be used within a sum as many times as it appears in the list, and a single number counts as a sum.) Your job is to solve this problem in general.
InputThe input will contain one or more test cases, one per line. Each test case contains t, the total, followed by n, the number of integers in the list, followed by n integers x1,…,xn. If n=0 it signals the end of the input; otherwise, t will be a positive integer less than 1000, n will be an integer between 1 and 12(inclusive), and x1,…,xn will be positive integers less than 100. All numbers will be separated by exactly one space. The numbers in each list appear in nonincreasing order, and there may be repetitions.
OutputFor each test case, first output a line containing ‘Sums of’, the total, and a colon. Then output each sum, one per line; if there are no sums, output the line ‘NONE’. The numbers within each sum must appear in nonincreasing order. A number may be repeated in the sum as many times as it was repeated in the original list. The sums themselves must be sorted in decreasing order based on the numbers appearing in the sum. In other words, the sums must be sorted by their first number; sums with the same first number must be sorted by their second number; sums with the same first two numbers must be sorted by their third number; and so on. Within each test case, all sums must be distince; the same sum connot appear twice.
Sample Input
4 6 4 3 2 2 1 1
5 3 2 1 1
400 12 50 50 50 50 50 50 25 25 25 25 25 25
0 0
Sample Output
Sums of 4:
4
3+1
2+2
2+1+1
Sums of 5:
NONE
Sums of 400:
50+50+50+50+50+50+25+25+25+25
50+50+50+50+50+25+25+25+25+25+25
题目意思:给一个数N,和K个数,求后面多个数之和等于N的所有情况;
解法:用DFS找出所有可能性,注意去掉相同的情况,搜索用常规dfs思路,注意去重,网上去重方法看不懂,用了自己好理解的string + stringstream + set 去重
string 利用 + 串新串
stringstream int -> string
set 去重
stringstream是个很好用的东东,不懂的小可爱们可以去看一下这篇文章https://blog.csdn.net/qq_33969563/article/details/109391029
AC 代码(1)当时在网上搜的没看懂人家是怎么去重的 = =
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int n,len,a[20],b[20],cnt;
int cmp(int a,int b)
{
return a>b;
}
void dfs(int x,int posa,int sum,int posb)
{
int i;
if(sum>n)
return;
if(sum == n)
{
cnt++;
for(i = 0; i<posb; i++)
{
if(i)
printf("+%d",b[i]);
else
printf("%d",b[i]);
}
printf("\n");
}
for(i = posa; i<len; i++)
{
b[posb] = a[i];
dfs(a[i],i+1,sum+a[i],posb+1);
while(i+1<len && a[i] == a[i+1])//搜索完毕后,若下一个搜索的数仍与当前相同,则跳过直至不相同
i++;
}
}
int main()
{
int i;
while(~scanf("%d%d",&n,&len),n+len!=0)
{
for(i = 0; i<len; i++)
scanf("%d",&a[i]);
sort(a,a+len,cmp);
printf("Sums of %d:\n",n);
cnt = 0;
dfs(0,0,0,0);
if(!cnt)
printf("NONE\n");
}
return 0;
}
AC代码(2) 自己写的,用的是上面分析的那种方法
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <set>
using namespace std;
int n, m;
bool flag = 0;
bool vis[110];
int ar[110], br[110];//ar存所有的数,br存选中的数
int kk = 0;
string str, s;
stringstream ss;
set<string> st;
bool cmp(int x, int y)
{
return x > y;
}
void dfs(int sum, int sta)
{
if(sum > n)//数大了,回溯
return ;
if(sum == n)//符合,标记flag,串字符串
{
flag = 1;
ss.clear();//清空字符流,多组流入之前必清空
ss<<br[0];//int 流成 string
ss>>s;
str = s;
for(int i = 1; i < kk; i++)
{
str = str + '+';//string 利用 + 可串新串
ss.clear();
ss<<br[i];
ss>>s;
str = str + s;
}
if(st.count(str) == 0)//去重,判断有无,无责输出,并插入set中
{
cout<<str<<endl;
st.insert(str);
}
return ;
}
for(int i = sta; i < m; i++)
{
if(!vis[i])
{
vis[i] = 1;
br[kk++] = ar[i];
dfs(sum + ar[i], i + 1);
vis[i] = 0;//取消标记,并将已储存数字个数减1;
kk--;
}
}
}
int main()
{
while(~scanf("%d %d",&n, &m) && n + m)
{
flag = 0;
memset(vis, 0, sizeof(vis));//数组清空
kk = 0;
for(int i = 0; i < m; i++)
{
scanf("%d",&ar[i]);
}
sort(ar, ar + m, cmp);
printf("Sums of %d:\n", n);
dfs(0, 0);
if(flag == 0)
cout<<"NONE"<<endl;
}
return 0;
}
如果你觉得该文对你学习搜索有帮助,拜托点一下一键三连呗,反正又不花钱。
拜托了,这对我真的很重要。