A - 棋盘问题
https://vjudge.ppsucxtt.cn/contest/65959#problem/A
思想:dfs搜是否不同行不同列的方案,主要的步骤在于vis数组的标记和释放的顺序。
#include <iostream>
#include <stdio.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 10;
int n, k, sum;
char mp[maxn][maxn]; //存图
int vis[maxn];//标记数组
void dfs(int x, int y){
if(y==0){ //当要放的棋子数为0时,方案数+1,sum++
sum++;
return;
}
if(x>n) return; //有可能dfs到x>n,这个直接返回
for(int i=1; i<=n; i++){
if(!vis[i]&&mp[x][i]=='#'){
vis[i]=1;//以列进行标记
dfs(x+1,y-1);
vis[i]=0;//释放标记,很妙,直接重置标记
}
}
if(x+1<=n) dfs(x+1,y);//这个是为了防止第一层没有一个#的情况
}
int main(){
while(cin>>n>>k&&n!=-1&&k!=-1){
for(int i=1; i<=n; i++) scanf("%s",mp[i]+1);//一种新的输入方法
sum=0;
dfs(1,k);
cout << sum << endl;
}
return 0;
}
B-Dungeon Master
题目链接
#include <iostream>
#include <queue>
#include <stdio.h>
#include <cstring>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 35;
int l, r, c;
char mp[maxn][maxn][maxn];
int dx[6]={1,-1,0,0,0,0};//方向数组,可以分开来写
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,1,-1};
int dis[maxn][maxn][maxn];//用于记录到达当前点所走的步数
struct node{
int x, y, z; //辅助结构体
};
queue <node> q;
node st, sf, ans, cnt;
int bfs(node st, node sf){
memset(dis,-1,sizeof(dis)); //多次输入,所以要重置
q.push(st);
dis[st.x][st.y][st.z]=0; //先设为0,从而所有的步数由此展开,-1则是未到达过的点,实现标记数组的功能
while(!q.empty()){
ans=q.front();
q.pop();
for(int i=0; i<6; i++){
int tx=ans.x+dx[i], ty=ans.y+dy[i], tz=ans.z+dz[i];
//这里是bfs的实现
if(tx>=1&&tx<=l&&ty>=1&&ty<=r&&tz>=1&&tz<=c&&dis[tx][ty][tz]==-1&&mp[tx][ty][tz]=='.'){
node cnt;
cnt.x=tx, cnt.y=ty, cnt.z=tz;
q.push(cnt);
dis[tx][ty][tz]=dis[ans.x][ans.y][ans.z]+1;
}
}
}
return dis[sf.x][sf.y][sf.z];
}
int main(){
while(scanf("%d%d%d",&l,&r,&c)&&l!=0&&r!=0&&c!=0){
for(int i=1; i<=l; i++){
for(int j=1; j<=r; j++){
for(int k=1; k<=c; k++){
cin >> mp[i][j][k];//不要用scanf的方式输入,不行
}
}
}
for(int i=1; i<=l; i++){
for(int j=1; j<=r; j++){
for(int k=1; k<=c; k++){
if(mp[i][j][k]=='S'){
st={i,j,k};
}
if(mp[i][j][k]=='E'){
sf={i,j,k};
mp[i][j][k]='.';//一定要将终点置为'.',这样才可以获得最后的步数,否则永远为-1
}
}
}
}
int res=bfs(st,sf);
if(res!=-1) printf("Escaped in %d minute(s).\n",res);
else printf("Trapped!\n");
}
return 0;
}
C-Catch That Cow (bfs找最短思想)
题目链接
#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn = 1e5+10;
int n, k;
struct node{
int x;
int sum;
};//辅助结构体
queue <node> q;
int vis[maxn]; //这题一定要有标记数组,否则会一直陷入死循环重复算一个位置,得不到结果
int bfs(int n, int k){
node ans, ansx, cnt;
ans.x=n, ans.sum=0;
q.push(ans);
vis[ans.x]=1;
while(!q.empty()){
cnt=q.front();
q.pop();//bfs一定要记住出队
if(cnt.x==k) return cnt.sum;
for(int i=1; i<=3; i++){
if(i==1) ansx.x=cnt.x+1;
if(i==2) ansx.x=cnt.x-1;
if(i==3) ansx.x=cnt.x*2;
if(ansx.x>=0&&ansx.x<=100000&&!vis[ansx.x]){
ansx.sum=cnt.sum+1;
vis[ansx.x]=1;
q.push(ansx);
}
}
}
}
int main(){
scanf("%d%d",&n,&k);
printf("%d",bfs(n,k));
return 0;
}
D-迷宫问题
题目链接
#include <iostream>
#include <vector>
#include <queue>
#include <stdio.h>
using namespace std;
typedef pair<int,int> pii;
int mp[6][6];
int vis[6][6];
int dir[4][2]={-1,0,1,0,0,-1,0,1};
int road[6][6][2];
vector <pii> v;
queue <pii> q;
void bfs(int x, int y){
vis[x][y]=1;
q.push({0,0});
while(!q.empty()){
int a=q.front().first, b=q.front().second;
q.pop();
if(a==4&&b==4){
int ax=a, by=b;
while(!(ax==0&&by==0)){
//一定要注意这里,会更改原始的数据,所以如果不用新变量去替换是会出错的
int c=ax, d=by;
v.push_back({ax,by});
ax=road[c][d][0];
by=road[c][d][1];
}
v.push_back({0,0});
for(int i=v.size()-1; i>=0; i--){
printf("(%d, %d)\n", v[i].first, v[i].second);
}
return ;
}
for(int i=0; i<4; i++){
int tx=a+dir[i][0], ty=b+dir[i][1];
if(tx>=0&&tx<=4&&ty>=0&&ty<=4&&mp[tx][ty]==0&&!vis[tx][ty]){
q.push({tx,ty});
vis[tx][ty]=1;
road[tx][ty][0]=a;
road[tx][ty][1]=b;
}
}
}
}
int main(){
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
cin >> mp[i][j];
bfs(0,0);
return 0;
}
E-Find The Multiple
题目链接
思路:
对于由0和1组成的10进制数,无非2个操作:
1、x10
2、x10+1
我们可以借助队列实现,注意x一定是队首元素!!!!
#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
typedef long long ll;
queue <ll> q;
int n;
void bfs(ll x){
while(!q.empty()) q.pop();
q.push(x);
while(!q.empty()){
ll y=q.front();
q.pop();
if(y%n==0){
printf("%lld\n",y);
return;
}
q.push(y*10);
q.push(y*10+1);
}
}
int main(){
while(cin >> n && n!=0){
bfs(1);
}
return 0;
}
F-Fire!
题目链接
思路:
1、火在追JOE,所以JOE会不会被火烧到取决于JOE到达一点的时间是否小于火到达此点的时间,因此,可以发现这是一个优先级的问题,所以用BFS(一般迷宫问题都用BFS)。
2、两个BFS同时进行,我们要考虑优先级,火追人,因此先对火BFS,再对JOE进行BFS。
3、注意每个BFS的条件,同时注意不止一个火源!!!!
4、逃出去的条件是到达边界
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1005;
char mp[maxn][maxn];
int dir[4][2]={-1,0,1,0,0,-1,0,1};
int vis[maxn][maxn]; //记录JOE出发点到该点的最短时间
int fire[maxn][maxn];//记录火所到达点的时间
int t, r, c;
int sx, sy;//JOE的初始位置
queue <pii> f;
queue <pii> q;
void BfsFire(){
while(!f.empty()) f.pop();//记得弹干净
memset(fire,-1,sizeof(fire));
for(int i=1; i<=r; i++){
for(int j=1; j<=c; j++){
if(mp[i][j]=='F'){//多个火源
f.push({i,j});
fire[i][j]=0;
}
}
}
while(!f.empty()){
int x=f.front().first;
int y=f.front().second;
f.pop();
for(int i=0; i<4; i++){
int tx=x+dir[i][0], ty=y+dir[i][1];
//注意条件,fire相当于平时的vis数组
if(tx>=1&&tx<=r&&ty>=1&&ty<=c&&mp[tx][ty]!='#'&&fire[tx][ty]==-1){
fire[tx][ty]=fire[x][y]+1;
f.push({tx,ty});
}
}
}
}
int MainBfs(int sx, int sy){
while(!q.empty()) q.pop();
memset(vis,-1,sizeof(vis));
vis[sx][sy]=0;
q.push({sx,sy});
while(!q.empty()){
int x=q.front().first, y=q.front().second;
q.pop();
if(x==1||y==1||x==r||y==c) return vis[x][y]+1;
for(int i=0; i<4; i++){
int tx=x+dir[i][0], ty=y+dir[i][1];
/*注意此处的条件
(vis[x][y]+1<fire[tx][ty]||fire[tx][ty]==-1)
这个条件是最坑的,因为有可能火走不到的地方是因为被#围起来了
而J恰好就可以走到*/
if(tx>=1&&tx<=r&&ty>=1&&ty<=c&&vis[tx][ty]==-1&&(vis[x][y]+1<fire[tx][ty]||fire[tx][ty]==-1)&&mp[tx][ty]!='#'){
q.push({tx,ty});
vis[tx][ty]=vis[x][y]+1;
}
}
}
return -1;
}
int main(){
cin >> t;
while(t--){
cin >> r >> c;
for(int i=1; i<=r; i++){
for(int j=1; j<=c; j++){
cin >> mp[i][j];
if(mp[i][j]=='J') sx=i,sy=j;
}
}
BfsFire();
int res=MainBfs(sx,sy);
if(res!=-1) printf("%d\n",res);
else puts("IMPOSSIBLE");
}
return 0;
}
G-非常可乐
思路:
首先,必须明白2点:
1、 可乐的总和是奇数时,不可能平分。
2、我们可以把最终平分的结果看做是可乐瓶本身与两个杯子较大的那个中的可乐相等(可以自行证明)。
2、其次,找最少的倒次数,那么一定是用BFS了,分为6种情况:
s->x
s->y
x->y
y->x
y->s
x->s
分别具体情况具体BFS即可,注意倒满和倒不满两种情况。
3、对于标记数组,我们不用开3维,可以直接用2维。
#include <bits/stdc++.h>
#define MAX INF
using namespace std;
typedef long long ll;
const int maxn = 2e2+10;
int n, m, l, g;
int vis[maxn][maxn];
struct node{
int s, x, y;
int step;
}; //储存3个杯中可乐数量,以及当前所倒的次数
node u, t, k;
int bfs(){
queue <node> q;//开在里面,因为要重置队列
k.s=l, k.x=0, k.y=0, k.step=0;
q.push(k);
vis[k.s][k.x]=1;
while(!q.empty()){
u=q.front();
q.pop();
if(u.s==l/2&&u.x==l/2) return u.step;//结束的标志
for(int i=0; i<6; i++){
if(i==0){//s->x
if(u.s+u.x>m){ //满的情况
t.x=m;
t.s=u.s-(m-u.x);
}
else{ //未满的情况
t.x=u.s+u.x;
t.s=0;
}
t.y=u.y;
}
if(i==1){//s->y
if(u.s+u.y>n){
t.y=n;
t.s=u.s-(n-u.y);
}
else{
t.y=u.s+u.y;
t.s=0;
}
t.x=u.x;
}
if(i==2){//x->y
if(u.x+u.y>n){
t.y=n;
t.x=u.x-(n-u.y);
}
else{
t.y=u.x+u.y;
t.x=0;
}
t.s=u.s;
}
if(i==3){//y->x
if(u.x+u.y>m){
t.x=m;
t.y=u.y-(m-u.x);
}
else{
t.x=u.x+u.y;
t.y=0;
}
t.s=u.s;
}
if(i==4){//y->s
t.y=0;
t.x=u.x;
t.s=u.s+u.y;
}
if(i==5){//x->s
t.x=0;
t.y=u.y;
t.s=u.s+u.x;
}
if(vis[t.s][t.x]) continue;
t.step=u.step+1;
vis[t.s][t.x]=1;
q.push(t);
}
}
return 0; //一定要+上,因为后面靠这个输出答案
}
int main(){
cin >> g;
while(g--){
cin >> l >> m >> n;
if(l%2){
printf("NO\n");
continue;
}
memset(vis,0,sizeof(vis));
if(m<n) swap(m,n);
int res=bfs();
if(res) printf("%d\n",res);
else printf("NO\n");
}
return 0;
}
H-Find a way
题目链接
思路:两个人用最短的时间到达同一家KFC,毫无疑问“最短”2字告诉我们要用BFS,所以我们可以对2个人进行全图的BFS(‘#’是走不通的),然后用disa和disb数组记录下两个人到达KFC时的步数,最后再寻找最短的步数即可。
#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e2+10;
int n,m,ax,ay,bx,by;
int vis[maxn][maxn];
char mp[maxn][maxn];
int disa[maxn][maxn];
int disb[maxn][maxn];
int dir[4][2]={-1,0,1,0,0,-1,0,1};
struct node{
int x,y;
int sum; //当前步数
}a,b,c,d;
void bfs(int ax, int ay, int bx, int by){
queue <node> q1;
queue <node> q2;
memset(disa,0,sizeof(disa));
memset(disb,0,sizeof(disb));
memset(vis,0,sizeof(vis));
a.x=ax, a.y=ay, a.sum=0;
b.x=bx, b.y=by, b.sum=0;
q1.push(a), q2.push(b);
while(!q1.empty()){
c=q1.front();
q1.pop();
if(mp[c.x][c.y]=='@') disa[c.x][c.y]=c.sum;
for(int i=0; i<4; i++){
d.x=c.x+dir[i][0], d.y=c.y+dir[i][1];
if(d.x>=0&&d.x<n&&d.y>=0&&d.y<m&&mp[d.x][d.y]!='#'&&!vis[d.x][d.y]){
d.sum=c.sum+1;
q1.push(d);
vis[d.x][d.y]=1;
}
}
}
memset(vis,0,sizeof(vis));//一定要重置
while(!q2.empty()){
c=q2.front();
q2.pop();
if(mp[c.x][c.y]=='@') disb[c.x][c.y]=c.sum;
for(int i=0; i<4; i++){
d.x=c.x+dir[i][0], d.y=c.y+dir[i][1];
if(d.x>=0&&d.x<n&&d.y>=0&&d.y<m&&mp[d.x][d.y]!='#'&&!vis[d.x][d.y]){
d.sum=c.sum+1;
q2.push(d);
vis[d.x][d.y]=1;
}
}
}
}
int main(){
while(cin>>n>>m){
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> mp[i][j];
if(mp[i][j]=='Y') ax=i,ay=j;
if(mp[i][j]=='M') bx=i,by=j;
}
}
bfs(ax,ay,bx,by);
int ans=MAX;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(disa[i][j]!=0&&disb[i][j]!=0){
if(ans>disa[i][j]+disb[i][j]) ans=disa[i][j]+disb[i][j];
}
}
}
printf("%d\n",ans*11);
}
return 0;
}
其实也可以用两次BFS,不用写那么困难。
I-Pots
题目链接
思路:首先找最少的操作次数,那么必然是bfs;
观察发现每轮一共有6次操作,和G题的非常可乐十分相似;
!难点:最后要输出操作步骤,即记录操作步骤,我们可以定义一个结构体数组,用于记录当前2杯中水的体积,操作步数以及操作方式,同时可用其中的操作步数是否==0作为常用的vis数组进行判断是否bfs过。然后在dfs寻结构体数组的根,最终一项一项的输出。
#include <iostream>
#include <queue>
#include <stdio.h>
#include <cstring>
using namespace std;
const int maxn = 110;
int a, b, c;
char s[10][10]={"FILL(1)","FILL(2)","DROP(1)","DROP(2)","POUR(1,2)","POUR(2,1)"};
struct node{
int sum1, sum2, op, step;
}v[maxn][maxn];//1中体积,2中体积,操作方式,操作步数
struct nodex{
int x, y;
}m,n;//这个用来入队进行bfs,存的是当前1、2杯中的体积
void dfs(int x, int y){
if(x==0&&y==0) return;
dfs(v[x][y].sum1,v[x][y].sum2);
printf("%s\n",s[v[x][y].op]);
}
void bfs(){
m.x=0, m.y=0;
v[0][0].step=1;
queue <nodex> q;
q.push(m);
while(!q.empty()){
n=q.front();
q.pop();
if(n.x==c||n.y==c){
printf("%d\n",v[n.x][n.y].step-1);
dfs(n.x,n.y);
return;
}
for(int i=1; i<=6; i++){
m=n;
if(i==1) m.x=a;
else if(i==2) m.y=b;
else if(i==3) m.x=0;
else if(i==4) m.y=0;
//对于以下的步骤,一定要注意顺序
else if(i==5){
if(m.x+m.y<=b){
//这里两步千万不能反!!!!!(我一直WA)
m.y=m.x+m.y;
m.x=0;
}
else{
m.x-=(b-m.y);
m.y=b;
}
}
else{
if(m.x+m.y<=a){
m.x=m.x+m.y;
m.y=0;
}
else{
m.y-=(a-m.x);
m.x=a;
}
}
if(v[m.x][m.y].step==0){
v[m.x][m.y].sum1=n.x;
v[m.x][m.y].sum2=n.y;
v[m.x][m.y].op=i-1;
v[m.x][m.y].step=v[n.x][n.y].step+1;
q.push(m);
}
}
}
printf("impossible\n");
}
int main(){
cin >> a >> b >> c;
bfs();
return 0;
}