kuangbin专题一简单搜索
——————————分割线——————————
2020-01-27更新
快要考研了,在acm里面也没什么成就,唉
下学期还要参加蓝桥杯,就趁寒假在家复(yu)习考研课程的间隙练一练题吧,蓝桥杯报的Java组,水一个国奖算了,大学四年最后两个愿望了,蓝桥杯拿个国奖+考研顺利,加油!!!
2020-0202更新
终于把暑假挖的坑给填上了,接下来要开始专题四,这几天都没有背单词看高数,得多看一看了。
——————————分割线——————————
题目列表
- POJ 1321 棋盘问题 AC: 2018-07-17 23:54:31
- POJ 2251 Dungeon Master AC: 2019-09-19 18:32:04
- POJ 3278 Catch That Cow AC: 2019-09-21 19:37:10
- POJ 3279 Fliptile AC: 2019-09-22 16:09:25
- POJ 1426 Find The Multiple AC: 2019-09-23 20:09:26
- POJ 3126 Prime Path AC: 2019-09-24 22:48:09
- POJ 3087 Shuffle'm Up AC: 2020-01-31 18:33:39
- POJ 3414 Pots AC: 2020-02-01 18:51:43
- FZU 2150 Fire Game 待定,oj挂了
- UVA 11624 Fire! AC: 2020-02-02 16:13:29
- POJ 3984 迷宫问题 AC: 2020-01-27 22:42:36
- HDU 1241 Oil Deposits AC: 2020-02-02 17:05:05
- HDU 1495 非常可乐 AC: 2020-01-31 16:58:53
- HDU 2612 Find a way AC: 2020-02-02 17:58:08
POJ-1321 棋盘问题
题意
就是在棋盘上放棋子,放的时候不能同行同列,和八皇后问题类似,只不过空白的地方不能放。这是我去年暑假acm训练时写的,现在已经忘了当时咋写的了,看着自己WA了好多发,太菜了。。。dfs时记得复原。
题解
dfs吧,没什么可说的,八皇后模板题。好烦啊,POJ不能用万能头文件。。。
代码
#include <iostream>
//#include <bits/stdc++.h>
#include <string.h>
typedef long long ll;
const int INF = 1 << 30;
const int maxn = 1e5;
using namespace std;
int n, m, ans;
char pan[10][10];
bool vis[10];
int cnt;
void dfs(int l)
{
if(cnt == m)
{
ans++;
return;
}
else if(l>=n)
return;
else
{
for(int i=0; i<n; i++)
if(pan[l][i]=='#' && !vis[i])
{
cnt++;
vis[i]=1;
dfs(l+1);
vis[i]=0;
cnt--;
}
dfs(l+1);
}
return;
}
int main()
{
//ios::sync_with_stdio(0), cin.tie(0);
while(cin >>n >>m)
{
if(n==-1 && m==-1)
break;
memset(vis, 0, sizeof(vis));
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
cin >>pan[i][j];
ans = cnt = 0;
dfs(0);
cout <<ans <<endl;
}
return 0;
}
POJ-2251 Dungeon Master
题意
一个3D迷宫,总共六个方向,上下左右前后。有的地方不能走有的能走,给出起点终点,问你能不能走到终点,如果能的话应该是求用时最短的吧(题目中没说,一开始用dfsWA了,后来看别人都用bfs才发现是不是要求最短路)。没移动一次要花费一分钟。
题解
bfs,一开始用bfs一直内存超限,快给我郁闷死了。。。要把走过的点用#堵上,防止多余的结点入队列。
代码
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
using namespace std;
const int maxn = 31;
struct node
{
int x, y, z, step;
};
node make_node(int x, int y, int z, int step)
{
node a;
a.x = x, a.y = y, a.z = z, a.step=step;
return a;
}
int main()
{
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};
while (cin >>l >>r >>c && l+r+c)
{
int x, y, z;
queue<node> q;
for(int i=0; i<l; i++)
{
for(int j=0; j<r; j++)
for(int k=0; k<c; k++)
{
cin >>mp[i][j][k];
if(mp[i][j][k]=='S')
q.push(make_node(i, j, k, 0));
if(mp[i][j][k]=='E')
{
x = i;
y = j;
z = k;
}
}
getchar();
}
bool flag = false;
while(!q.empty())
{
node a = q.front();
q.pop();
if(a.x==x && a.y==y && a.z==z)
{
flag = true;
cout <<"Escaped in "<<a.step <<" minute(s)." <<endl;
break;
}
for (int i = 0; i < 6; ++i)
{
if(mp[a.x+dx[i]][a.y+dy[i]][a.z+dz[i]]!='#')
if(a.x+dx[i]>=0 && a.x+dx[i]<l && a.y+dy[i]>=0 && a.y+dy[i]<r && a.z+dz[i]>=0 && a.z+dz[i]<c)
{
q.push(make_node(a.x+dx[i], a.y+dy[i], a.z+dz[i], a.step+1));
mp[a.x+dx[i]][a.y+dy[i]][a.z+dz[i]] = '#';
}
}
}
if(flag)
continue;
else
cout <<"Trapped!" <<endl;
}
return 0;
}
POJ-3287 Catch That Cow
题意
你的牛跑了,要把它抓回来。你的位置在n,牛的位置在k,牛不会跑,你要用最少的时间走到牛的位置。你们的位置都在同一个数轴上,只有左右两个方向,你有三种走法,左走一步、右走一步或者传送到你当前坐标的二倍的位置,这三种走法都只花费一分钟。n和k都小于等于100000
题解
只有三种操作,求最短时间,可以用bfs。bfs要设置一个vis数组,注意不能数组越界。还要注意当n==k的时候。一开始一直超内存,后来发现要设一个vis数组剪枝。
代码
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
const int maxn = 1e5+10;
bool vis[maxn];
struct node
{
int p, t;
};
node make_node(int p, int t)
{
node a;
a.p = p;
a.t = t;
return a;
}
int main()
{
int n, k;
while(cin >>n >>k)
{
if(n==k)
{
cout <<0 <<endl;
continue;
}
memset(vis, 0, sizeof vis);
queue<node> q;
q.push(make_node(n, 0));
vis[n] = true;
while(!q.empty())
{
node a = q.front();
q.pop();
if(a.p+1==k || a.p-1==k || a.p*2==k)
{
cout <<a.t+1 <<endl;
break;
}
if(a.p<k)
{
if(a.p+1<=100000 && !vis[a.p+1])
{
q.push(make_node(a.p+1, a.t+1));
vis[a.p+1] = true;
}
if(a.p*2-k<k-a.p && a.p*2<=100000 && !vis[a.p*2])
{
q.push(make_node(a.p*2, a.t+1));
vis[a.p*2] = true;
}
if(a.p-1>=0 && !vis[a.p-1])
{
q.push(make_node(a.p-1, a.t+1));
vis[a.p-1] = true;
}
}
else if(a.p>k && a.p-1>=0 && !vis[a.p-1])
{
q.push(make_node(a.p-1, a.t+1));
vis[a.p-1] = true;
}
}
}
return 0;
}
POJ-3279 Fliptile
题意
有n*m个方格,每个方格都有一块瓷砖,瓷砖的两面分别是黑色和白色,当你翻转瓷砖时瓷砖会从白色翻转成黑色,或者从黑色翻转成白色。样例输入为每个方格瓷砖的初始颜色,0代表白色,1代表黑色。现在让你的奶牛来将所有格子翻成白色,由于奶牛的蹄子比较大,它每次反转的时会将相邻的瓷砖也翻转过来,即上下左右的瓷砖。最后输出每个瓷砖的翻转次数,要求总的翻转次数最少,如果答案有多种,则输出字典序最小的那个答案。
题解
这题有个最重要的思想,就是第i层如果有黑色瓷砖,要通过第i+1层的翻转将其翻转成白色。这样只用保证前一层没有黑色即可,不用管当前层翻转成什么样,也不用管下一层。这样的话只要第一层的状态确定,后面所有的状态都确定了。因为第二层需要将第一层的黑瓷砖翻成白色的,这个过程中第二层可能会出现黑瓷砖,再用第三层将第二层翻转成白色的,再用第四层将。。。我们可以枚举第一层所有的状态,即每一块瓷砖翻或不翻,总共有2^m个,然后用第二层将第一层翻成白色。。。用最后一层将倒数第二层翻成白色,因为后面没有瓷砖来翻转最后一层了,所有我们通过判断最后一层有没有黑色瓷砖,就可以判断这个方法是否可行。
输出次数最少的那一个可以每次翻转记录一下,最后对比所有可能性的翻转次数取最小即可。
字典序最小,即输出答案时每行的字典序最小,每行的字典序即把所有数字串成一个字符串,让他们字典序最小,即将0 0 0 1 1串成00011,而00011的字典序比10010小。保证字典序最小可以先保证第一行字典序最小,第一行有2^m种可能,我们把没个瓷砖看成二进制的一位数,那第一行的所有操作就是0~(1<<m-1),我们枚举的时候直接从0开始枚举就行了。
一开始写的时候怎么想都没思路,最后看了别人的题解才写出来,还是做题太少了,太菜了。。。
代码
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 30;
const int INF = 1 << 30;
int n, m, turnnum, turnans;
int mp[maxn][maxn], a[maxn][maxn], fn[maxn][maxn], ans[maxn][maxn];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
void turn(int i, int j)
{
fn[i][j] = 1;
turnnum++;
a[i][j] = !a[i][j];
for (int k = 0; k < 4; k++)
a[i + dx[k]][j + dy[k]] = !a[i + dx[k]][j + dy[k]];
}
int main()
{
while (cin >>n >>m)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> mp[i][j];
}
}
int lp = 1 << m;
turnans = INF;
for (int i = 0; i < lp; i++)
{
memset(fn, 0, sizeof fn);
memcpy(a, mp, sizeof mp);
turnnum = 0;
int p = i;
for (int j = 1; j <= m; j++)
{
if (p & 1)
turn(1, j);
p >>= 1;
}
for (int j = 2; j <= n; j++)
{
for (int k = 1; k <= m; k++)
{
if (a[j - 1][k])
turn(j, k);
}
}
int j;
for (j = 1; j <= m; j++)
{
if (a[n][j])
break;
}
if (j == m+1 && turnnum < turnans)
{
turnans = turnnum;
memcpy(ans, fn, sizeof fn);
}
}
if (turnans == INF)
cout << "IMPOSSIBLE" << endl;
else
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cout << ans[i][j] << ' ';
}cout << endl;
}
}
}
return 0;
}
参考
POJ-1426 Find The Multiple
题意
给你一个不超过200的数n,让你求一个数m,m不超过100位,m只包含0和1且(m%n)== 0。
题解
不知道为什么,直接定义longlong + BFS就过了。。。一开始想模拟,还超时了,菜。。。
在网上看到有个大佬在用这题讲同余模定理,抽空看一下。
代码
#include <iostream>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e5+10;
queue<int> q;
int main()
{
unsigned ll n, m=1;
while(cin >>n && n)
{
while(!q.empty())q.pop();
m = 1;
q.push(m);
if(m%n==0)
{
cout <<m <<endl;
continue;
}
while(!q.empty())
{
m = q.front();
q.pop();
m *= 10;
if(m%n==0)
{
cout <<m <<endl;
break;
}
q.push(m);
m += 1;
if(m%n==0)
{
cout <<m <<endl;
break;
}
q.push(m);
}
}
return 0;
}
POJ-3126 Prime Path
题意
测试数据不超过一百组,每组给出两个四位素数,要求你将第一个数字变换成第二个数字。没回只能变换一位数字,而且首位数字不能变成0,变换过的数字也必须为素数。每次变换需要花1磅,问你从第一个数字变换到第二个数字最小花费是多少。
题解
最少花费,肯定BFS了,先用线性素数筛打个表,每次进队的数据查表时间复杂度为O(1),然后每一层循环,要把四位数字全换一遍,换一个数字进一次队列,用vis数组剪枝。
代码
#include <iostream>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e5+10;
int prime[maxn], pNum = 0;
bool p[maxn] = {false}, vis[maxn]={false};
struct num
{
int a, cos;
};
num make_num(int a, int cos)
{
num x;
x.a = a;
x.cos = cos;
return x;
}
void eulerSieve(int n)
{
for (int i = 2; i <= n; i++)
{
if (p[i] == false)
prime[pNum++] = i;
for (int j = 0; j < pNum; j++)
{
if (i * prime[j] > n)
break;
p[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
}
int main()
{
eulerSieve(100000);
int t;
cin >>t;
while(t--)
{
memset(vis, false, sizeof vis);
int a, b;
cin >>a >>b;
queue<num> q;
q.push(make_num(a, 0));
vis[a] = true;
while(!q.empty())
{
num x = q.front();
q.pop();
if(x.a==b)
{
cout <<x.cos <<endl;
break;
}
for(int i=1; i<10; i++)
{
int y = x.a;
if(y/1000==i) continue;
y = i*1000+y%1000;
if(!vis[y] && !p[y])
{
q.push(make_num(y, x.cos+1));
vis[y] = true;
}
}
for(int i=0; i<10; i++)
{
int y = x.a;
if(y/100%10==i) continue;
y = (y/1000*10+i)*100+y%100;
if(!vis[y] && !p[y])
{
q.push(make_num(y, x.cos+1));
vis[y] = true;
}
}
for(int i=0; i<10; i++)
{
int y = x.a;
if(y%100/10==i) continue;
y = (y/100*10+i)*10+y%10;
if(!vis[y] && !p[y])
{
q.push(make_num(y, x.cos+1));
vis[y] = true;
}
}
for(int i=0; i<10; i++)
{
int y = x.a;
if(y%10==i) continue;
y = y/10*10+i;
if(!vis[y] && !p[y])
{
q.push(make_num(y, x.cos+1));
vis[y] = true;
}
}
}
}
return 0;
}
POJ-3087 Shuffle'm Up
题意
根本不是搜索题,就是简单的模拟题。。。
给你两组牌s1,s2,每组牌的数量相同,分为两个步骤洗牌和分牌。
洗牌:分别从s1和s2底部每次抽一张牌放到桌上,先从s2的底部开始抽,组成一组牌s12(类似于入栈操作)。
分牌:将组成的s12再分成s1和s2,将s12下半部分组成s1,将s12的上半部分组成s2。
牌的颜色总共有A~H种,现在又给一组牌s3,问经过几次洗牌能将牌洗成s3的样子,如果可以则输出数据组序号和洗牌次数,不能则输出数据组序号和-1 。(序号从1开始)
题解
一开始看到题要求最少的洗牌次数,以为要用bfs,然后在想怎么用bfs的时候发现他每一步的操作都只有固定的一种,没什么可搜索的啊,才发现就是个暴力模拟题。
就是用队列模拟洗牌和发牌的过程,如果不能洗成目标牌组的话会发现洗着洗着s1和s2又回到原来的样子了,这个手算一下样例的第二组数据就知道了。
代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
for (int i = 0; i < n; i++) {
int x = cin.nextInt();
String s1 = cin.nextLine();
s1 = cin.nextLine();
String s2 = cin.nextLine();
String s3 = cin.nextLine();
Queue<Character> queue = new LinkedList<Character>();
String s4 = s1, s5 = s2;
boolean flag = false;
int step = 0;
while (true) {
step++;
for (int j = 0; j < s1.length(); j++) {
queue.offer(s5.charAt(j));
queue.offer(s4.charAt(j));
}
String ss = "";
for (int j = 0; j < 2 * x; j++) {
ss += queue.peek();
queue.offer(queue.peek());
queue.poll();
}
if (ss.equals(s3)) {
flag = true;
break;
}
s4 = "";
s5 = "";
for (int j = 0; j < x; j++) {
s4+=queue.peek();
queue.poll();
}
for (int j = 0; j < x; j++) {
s5+=queue.peek();
queue.poll();
}
if (s4.equals(s1) && s5.equals(s2)){
break;
}
}
if (flag){
System.out.println(i+1 + " " + step);
}else {
System.out.println(i+1 + " " + -1);
}
}
}
}
POJ-3414 Pots
题意
这题和HDU1495非常可乐太像了,不过比那个复杂一些,操作方法变了一下,而且需要回溯记录路径。感觉是可乐问题+迷宫问题。。。
一开始有两个空罐子A和B,每个罐子都有固定容量(倒水题惯例),问你经过一系列操作是否可以使得任意一个罐子中的水有C升,如果可以则求最少操作次数和所有操作步骤,如果不可以则输出"impossible"。操作有如下几种:
- 将A或B灌满水
- 将A或B的水全部倒掉
- 将A的水倒给B,或将B的水倒给A
题解
求最少操作次数和操作步骤,用bfs+回溯,vis去重。(用java写这种题代码好长啊,而且感觉我写的代码好笨重)
代码
import java.util.*;
class Pot {//用于记录每个状态
int a, b;
int step;
public Pot(int a, int b, int step) {
this.a = a;
this.b = b;
this.step = step;
}
public Pot(Pot x) {
this.a = x.a;
this.b = x.b;
this.step = x.step;
}
}
class View {//vis数组
boolean v;
int op;//操作种类
int prex, prey;//前驱结点
}
public class Main {
static String[] opt = new String[]{//所有操作种类,方便输出
"FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"
};
static View[][] vis = new View[105][105];
public static void print(int x, int y) {
if (vis[x][y].prex != -1 && vis[x][y].prey != -1)
print(vis[x][y].prex, vis[x][y].prey);
if (vis[x][y].op != -1)
System.out.println(opt[vis[x][y].op]);
return;
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int A = cin.nextInt();
int B = cin.nextInt();
int C = cin.nextInt();
for (int i = 0; i < 105; i++) {
for (int j = 0; j < 105; j++) {
vis [i][j] = new View();
vis[i][j].v = false;
}
}
vis[0][0].v = true;
vis[0][0].op = -1;
vis[0][0].prex = -1;
vis[0][0].prey = -1;
Queue<Pot> queue = new LinkedList<Pot>();
Pot now = new Pot(0, 0, 0);
queue.offer(now);
boolean flag = false;
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
if (now.a == C || now.b == C) {
flag = true;
break;
}
for (int i = 0; i < 6; i++) {
Pot next = new Pot(now);
if (i == 0 && next.a != A && !vis[A][next.b].v) {
vis[A][next.b].v = true;
vis[A][next.b].op = 0;
vis[A][next.b].prex = now.a;
vis[A][next.b].prey = now.b;
next.a = A;
next.step++;
queue.offer(next);
continue;
}
if (i == 1 && next.b != B && !vis[next.a][B].v) {
vis[next.a][B].v = true;
vis[next.a][B].op = 1;
vis[next.a][B].prex = now.a;
vis[next.a][B].prey = now.b;
next.b = B;
next.step++;
queue.offer(next);
continue;
}
if (i == 2 && next.a != 0 && !vis[0][next.b].v) {
vis[0][next.b].v = true;
vis[0][next.b].op = 2;
vis[0][next.b].prex = now.a;
vis[0][next.b].prey = now.b;
next.a = 0;
next.step++;
queue.offer(next);
continue;
}
if (i == 3 && next.b != 0 && !vis[next.a][0].v) {
vis[next.a][0].v = true;
vis[next.a][0].op = 3;
vis[next.a][0].prex = now.a;
vis[next.a][0].prey = now.b;
next.b = 0;
next.step++;
queue.offer(next);
continue;
}
if (i == 4 && next.a != 0 && next.b != B) {
int vo = B - next.b;
if (next.a > vo) {
next.a -= vo;
next.b = B;
} else {
next.b += next.a;
next.a = 0;
}
if (!vis[next.a][next.b].v) {
vis[next.a][next.b].v = true;
vis[next.a][next.b].op = 4;
vis[next.a][next.b].prex = now.a;
vis[next.a][next.b].prey = now.b;
next.step++;
queue.offer(next);
}
continue;
}
if (i == 5 && next.a != A && next.b != 0) {
int vo = A - next.a;
if (next.b > vo) {
next.b -= vo;
next.a = A;
} else {
next.a += next.b;
next.b = 0;
}
if (!vis[next.a][next.b].v) {
vis[next.a][next.b].v = true;
vis[next.a][next.b].op = 5;
vis[next.a][next.b].prex = now.a;
vis[next.a][next.b].prey = now.b;
next.step++;
queue.offer(next);
}
continue;
}
}
}
if (flag){
System.out.println(now.step);
print(now.a, now.b);
}else {
System.out.println("impossible");
}
}
}
FZU-2150 Fire Game
题意
一个n×m的地图,有的地方是草,有的地方是空的,选两个点同时点火,问是否能将地图上的草烧完,如果是则求出最短燃烧时间。火烧到相邻带有草的格子所需要的时间为1,相邻表示上下左右的格子(即过一个时间,其上有左右有草的格子同时着火),选择的两个点可以相同
题解
双起点bfs,我的方法比较笨,先用bfs求出图中草有几个不相邻的分区,如果分区数大于2肯定不能完成,因为最多只能选择两个起点烧,如果两个一下,则将每个分区中的所有点分别加入一个队列,依次从该分区的每个点进行一次bfs遍历该分区,求出其中用时最短的。(由于oj挂了,我也没有测试,只过了样例,但是觉得这题应该没什么坑,而且数据也挺小的,总共才1e6,所以先写着,以后等可以交了我再改一下)
代码
import java.util.*;
class Point {
char ch;
int index, x, y, step;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
}
}
public class Main {
static int[] dx = {1, -1, 0, 0};
static int[] dy = {0, 0, 1, -1};
static void reset(Point[][] mp){
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
mp[i][j].step = 0;
}
}
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int t = cin.nextInt();
Point[][] mp = new Point[15][15];
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
mp[i][j] = new Point();
mp[i][j].x = i;
mp[i][j].y = j;
}
}
for (int i = 0; i < t; i++) {
int n = cin.nextInt(), m = cin.nextInt();
String s = cin.nextLine();
for (int j = 0; j < n; j++) {
s = cin.nextLine();
for (int k = 0; k < s.length(); k++) {
mp[j][k].ch = s.charAt(k);
mp[j][k].index = -1;
mp[j][k].step = 0;
}
}
Queue<Point> queue = new LinkedList<Point>();
Queue<Point> q1 = new LinkedList<Point>();
Queue<Point> q2 = new LinkedList<Point>();
int num = 0;
for (int j = 0; j < n; j++) {
for (int k = 0; k < m; k++) {
if (mp[j][k].ch == '#' && mp[j][k].index == -1) {
Point now = mp[j][k];
now.index = num;
if (num==0)q1.offer(now);
else q2.offer(now);
queue.offer(now);
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
for (int l = 0; l < 4; l++) {
if (now.x + dx[l] >= 0 && now.x + dx[l] < n && now.y + dy[l] >= 0 && now.y + dy[l] < m && mp[now.x + dx[l]][now.y + dy[l]].ch == '#' && mp[now.x + dx[l]][now.y + dy[l]].index==-1) {
mp[now.x + dx[l]][now.y + dy[l]].index = num;
queue.offer(mp[now.x + dx[l]][now.y + dy[l]]);
if (num==0)q1.offer(mp[now.x + dx[l]][now.y + dy[l]]);
else q2.offer(mp[now.x + dx[l]][now.y + dy[l]]);
}
}
}
num++;
}
}
}
if (num>2){
System.out.println("Case " + (i+1) + ": " + -1);
continue;
}
int t1=10000, t2=10000;
while (!q1.isEmpty()){
char[][] mmp = new char[15][15];
for (int j = 0; j < 15; j++) {
for (int k = 0; k < 15; k++) {
mmp[j][k] = mp[j][k].ch;
mp[j][k].step = 0;
}
}
while (!queue.isEmpty()) queue.poll();
Point now = q1.peek();
q1.poll();
queue.offer(now);
mmp[now.x][now.y] = '.';
while (!queue.isEmpty()){
now = queue.peek();
queue.poll();
for (int j = 0; j < 4; j++) {
if (now.x + dx[j] >= 0 && now.x + dx[j] < n && now.y + dy[j] >= 0 && now.y + dy[j] < m && mmp[now.x + dx[j]][now.y + dy[j]] == '#'){
mmp[now.x + dx[j]][now.y + dy[j]] = '.';
mp[now.x + dx[j]][now.y + dy[j]].step = now.step+1;
queue.offer(mp[now.x + dx[j]][now.y + dy[j]]);
}
}
}
t1 = Math.min(t1, now.step);
}
if (num==2){
while (!q2.isEmpty()){
char[][] mmp = new char[15][15];
for (int j = 0; j < 15; j++) {
for (int k = 0; k < 15; k++) {
mmp[j][k] = mp[j][k].ch;
mp[j][k].step = 0;
}
}
while (!queue.isEmpty()) queue.poll();
Point now = q2.peek();
q2.poll();
queue.offer(now);
mmp[now.x][now.y] = '.';
while (!queue.isEmpty()){
now = queue.peek();
queue.poll();
for (int j = 0; j < 4; j++) {
if (now.x + dx[j] >= 0 && now.x + dx[j] < n && now.y + dy[j] >= 0 && now.y + dy[j] < m && mmp[now.x + dx[j]][now.y + dy[j]] == '#'){
mmp[now.x + dx[j]][now.y + dy[j]] = '.';
mp[now.x + dx[j]][now.y + dy[j]].step = now.step+1;
queue.offer(mp[now.x + dx[j]][now.y + dy[j]]);
}
}
}
t2 = Math.min(t2, now.step);
}
}
if (t1==10000 || t2==10000)
System.out.println("Case " + (i+1) + ": " + Math.min(t1, t2));
else
System.out.println("Case " + (i+1) + ": " + Math.max(t1, t2));
}
}
}
UVA-11624 Fire!
题意
火烧屁股,赶紧窜。。。
逃出迷宫,只要是挨着迷宫边缘的点都可以逃出迷宫。
题解
注意,本题起始时间可能有多个起始火苗!!!(一开始我也没注意到,偶然间在vj的评论里看到的不然我可能也被坑死了)其实就是个bfs的题,数据量比较大,容易超时,要注意一点。应该让火苗先走人再走,我是让每个人走过的点和被火苗烧过的点都变成墙,这样后来走的时候就不会再走了。
我把火苗和人放在了两个队列里面了,一开始想复杂了,每个点都有一个属性step,用来记录当前时间,如果进入下一个时间点就让该时间点的火苗全部走一遍,然后再让人走。
后来看到别人让火苗和人放在一个队列里面了,只有一开始处理一下,先让所有起始的火苗先进队列,再让人的起始点进入队列就可以了。(不过我后来把我的代码改成这样超时了,就懒得再改了,一开始调试时用的输出代码忘记删了,他还给我判超时,我懵逼了好一会儿。。。)
代码
import java.util.*;
class Point {
int x, y, step;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point() {
}
public void setPoint(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
public Point(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
public Point(Point point) {
this.x = point.x;
this.y = point.y;
this.step = point.step;
}
}
public class Main {
static int[] dx = {1, -1, 0, 0};
static int[] dy = {0, 0, 1, -1};
static char[][] mp = new char[1005][1005];
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int t = cin.nextInt();
while (t-- > 0) {
int r = cin.nextInt(), c = cin.nextInt();
String s = cin.nextLine();
Queue<Point> Fire = new LinkedList<Point>();
Queue<Point> queue = new LinkedList<Point>();
Point now = new Point();
for (int i = 0; i < r; i++) {
s = cin.nextLine();
for (int j = 0; j < s.length(); j++) {
mp[i][j] = s.charAt(j);
if (mp[i][j] == 'F') {
mp[i][j] = '#';
Fire.offer(new Point(i, j, 0));
}
if (mp[i][j] == 'J') {
mp[i][j] = '#';
now.setPoint(i, j, 0);
queue.offer(now);
}
}
}
int CurStep = -1;
boolean flag = false;
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
if (now.y == 0 || now.y == c - 1 || now.x == 0 || now.x == r - 1) {
flag = true;
break;
}
if (now.step > CurStep) {//先烧火
CurStep++;
Point CurFire;
while (!Fire.isEmpty()) {
CurFire = Fire.peek();
if (CurFire.step != CurStep)
break;
Fire.poll();
for (int i = 0; i < 4; i++) {
Point next = new Point(CurFire);
next.step++;
next.x += dx[i];
next.y += dy[i];
if (next.x >= 0 && next.x < r && next.y >= 0 && next.y < c && mp[next.x][next.y] != '#') {
mp[next.x][next.y] = '#';
Fire.offer(next);
}
}
}
}
for (int i = 0; i < 4; i++) {
Point next = new Point(now);
next.step++;
next.x += dx[i];
next.y += dy[i];
if (next.x >= 0 && next.x < r && next.y >= 0 && next.y < c && mp[next.x][next.y] != '#') {
mp[next.x][next.y] = '#';
queue.offer(next);
}
}
}
if (flag) System.out.println(now.step+1);
else System.out.println("IMPOSSIBLE");
}
}
}
POJ-3984 迷宫问题
太久没写题了,先拿个简单的练练手吧
Java在OJ交题有好多坑啊,这有一篇比较不错的博客讲的挺全的:JAVA在OJ上提交程序的注意事项
(一定要带上包名,别问我为什么知道的(手动狗头)。用Java写题总感觉怪怪的)
题意
给一个5×5的迷宫,1是墙,0是路,求出从左上角到右下角的最短路径并输出路径上每个结点的坐标,只能横着走或竖着走,不能斜着走。
题解
用bfs求最短路嘛,再定义一个Point结构体,记录前驱结点,最后用递归输出路径。注意逗号后面有一个空格。
其实他这个题的测试数据只有一个,就是样例。。。我看vj上面有人说直接把样例输出就过了。。。
代码
import java.util.*;
class Point{
int x, y;
int prex, prey;
int dis;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public Point(){}
}
public class Main {
public static int[] dx = {1, -1, 0, 0};
public static int[] dy = {0, 0, 1, -1};
public static Point[][] p = new Point[5][5];
public static void print_point(int x, int y){
if(x==0 && y==0){
System.out.println("(" + x + ", " + y + ")");
return;
}
print_point(p[x][y].prex, p[x][y].prey);
System.out.println("(" + x + ", " + y + ")");
}
public static void main(String [] args){
int[][] mp = new int[5][5];
Scanner cin = new Scanner(System.in);
Queue<Point> q = new LinkedList<Point>();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
p[i][j] = new Point();
}
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
mp[i][j] = cin.nextInt();
}
}
Point point = new Point(0, 0);
point.dis = 0;
point.prex = 0;
point.prey = 0;
q.offer(point);
while (!q.isEmpty()){
Point pp = q.peek();
if (pp.x==4 && pp.y==4)
break;
for (int i = 0; i < 4; i++) {
int xx = pp.x + dx[i];
int yy = pp.y + dy[i];
if(xx>=0 && xx<5 && yy>=0 && yy<5 && mp[xx][yy]!=1){
mp[xx][yy] = 1;
p[xx][yy].x = xx;
p[xx][yy].y = yy;
p[xx][yy].prex = pp.x;
p[xx][yy].prey = pp.y;
p[xx][yy].dis = pp.dis+1;
q.offer(p[xx][yy]);
}
}
q.poll();
}
print_point(4, 4);
}
}
HDU-1241 Oil Deposits
题意
求分块儿数目
题解
注意第二个样例,相邻的意思是相邻的八个格不是四个格
好像用dfs和bfs都可以,习惯了用bfs我就写了bfs。
每找到一个油田,就用bfs遍历这个油田,将油田的所有‘@’变为‘*’就可以了。
代码
import java.util.*;
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(Point point) {
this.x = point.x;
this.y = point.y;
}
}
public class Main {
static int[] dx = {1, -1, 0, 0, 1, 1, -1, -1};
static int[] dy = {0, 0, 1, -1, 1, -1, 1, -1};
static char[][] mp = new char[105][105];
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
while (true) {
int n = cin.nextInt(), m = cin.nextInt();
if (n==0 || m==0) break;
int ans = 0;
String s = cin.nextLine();
Queue<Point> queue = new LinkedList<Point>();
for (int i = 0; i < n; i++) {
s = cin.nextLine();
for (int j = 0; j < s.length(); j++) {
mp[i][j] = s.charAt(j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mp[i][j] == '@'){
ans++;
Point now = new Point(i, j);
queue.offer(now);
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
for (int k = 0; k < 8; k++) {
Point next = new Point(now);
next.x += dx[k];
next.y += dy[k];
if (next.x >= 0 && next.x < n && next.y >= 0 && next.y < m && mp[next.x][next.y] == '@') {
mp[next.x][next.y] = '*';
queue.offer(next);
}
}
}
}
}
}
System.out.println(ans);
}
}
}
HDU-1495 非常可乐
题意
有三个杯子,s,n,m,每个被子都有自己的最大容量,初始时s装着满杯可乐。问最少经过几次倒水的步骤,能让其中两个杯子的可乐相等。由于每个杯子都没有刻度,所以每次倒水只能将一个杯子的可乐全部倒进另一个杯子,或者将另一个杯子倒满。(我一开始没理解这个,连样例都没看懂,一直在想他到底怎么倒水的)
题解
这题有两种解法,一种比较好想的bfs,另一种是数论(我不会。。。)给出网上dalao链接:HDU 1495 非常可乐(数论)
bfs解法:
当s中的初始为奇数时肯定不可平分。用一个类记录每个倒水的状态,包括当前每个杯子多少水和经过了多少步骤,每次倒水只有六种倒法:s->n,s->m,n->s,m->s,n->m,m->n,每次遍历这六种方法就行了。然后判断倒水的终止状态:任意两个杯子量相等且另一个杯子为空时才满足条件。
代码
import java.util.*;
class cola {
int[] cup = new int[3];
int step;
}
public class Main {
public static boolean[][][] vis = new boolean[105][105][105];
public static void main(String[] args) {
int s, n, m;
Scanner cin = new Scanner(System.in);
while (true) {
s = cin.nextInt();
n = cin.nextInt();
m = cin.nextInt();
if (s == 0 && n == 0 && m == 0) {
break;
}
if (s % 2 == 1) {
System.out.println("NO");
continue;
}
for (int i = 0; i < 105; i++) {
for (int j = 0; j < 105; j++) {
for (int k = 0; k < 105; k++) {
vis[i][j][k] = false;
}
}
}
vis[s][0][0] = true;
cola now = new cola();
now.cup[0] = s;
now.cup[1] = 0;
now.cup[2] = 0;
now.step = 0;
Queue<cola> queue = new LinkedList<cola>();
queue.offer(now);
boolean flag = false;
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
if ((now.cup[0]==now.cup[1] && now.cup[2]==0) || (now.cup[0]==now.cup[2] && now.cup[1]==0) || (now.cup[1]==now.cup[2] && now.cup[0]==0)){
System.out.println(now.step);
flag = true;
break;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i != j) {
cola next = new cola();
next.step = now.step;
System.arraycopy(now.cup, 0, next.cup, 0, 3);
int co = 0;
if (j == 0) co = s - next.cup[j];
if (j == 1) co = n - next.cup[j];
if (j == 2) co = m - next.cup[j];
if (next.cup[i] > co) {
next.cup[i] -= co;
next.cup[j] += co;
} else {
next.cup[j] += next.cup[i];
next.cup[i] = 0;
}
next.step += 1;
if (!vis[next.cup[0]][next.cup[1]][next.cup[2]]) {
vis[next.cup[0]][next.cup[1]][next.cup[2]] = true;
queue.offer(next);
}
}
}
}
}
if (!flag){
System.out.println("NO");
}
}
}
}
HDU-2612 Find a way
题意
地图上有好几个kfc(话说我也好久没吃kfc,好想吃kfc的薯条啊,不蘸番茄酱的。疫情太严重了,还是在家敲代码复习考研课吧。。。没事还能打一打无限火力,太快乐了),要求出两个起点到kfc最短的时间,移动到上下左右相邻结点要花的时间是11(为啥是11,是不是出题人单身太久了)。题目保证一定有解
题解
又是bfs,bfs,bfs。。。从两个点分别做一次bfs,要注意两个人不是同时出发的,到达时间是两个人所花时间之和。
代码
import java.util.*;
class Point {
int x, y, step;
public Point(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(Point point) {
this.x = point.x;
this.y = point.y;
this.step = point.step;
}
}
public class Main {
static int[] dx = {1, -1, 0, 0};
static int[] dy = {0, 0, 1, -1};
static char[][] mp = new char[205][205];
static boolean[][] vis = new boolean[205][205];
static int[][] KFC = new int[205][205];//记录每个kfc的时间
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
while (cin.hasNextInt()) {
int n = cin.nextInt(), m = cin.nextInt();
int ans = Integer.MAX_VALUE;
int yx = 0, yy = 0, mx = 0, my = 0;
String s = cin.nextLine();
Queue<Point> queue = new LinkedList<Point>();
for (int i = 0; i < n; i++) {
s = cin.nextLine();
for (int j = 0; j < s.length(); j++) {
mp[i][j] = s.charAt(j);
vis[i][j] = false;
KFC[i][j] = 0;
if (mp[i][j] == 'Y') {
yx = i;
yy = j;
}
if (mp[i][j] == 'M') {
mx = i;
my = j;
}
}
}
Point now = new Point(yx, yy, 0);
queue.offer(now);
vis[yx][yy] = true;
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
if (mp[now.x][now.y] == '@') KFC[now.x][now.y] = now.step * 11;
for (int k = 0; k < 4; k++) {
Point next = new Point(now);
next.step++;
next.x += dx[k];
next.y += dy[k];
if (next.x >= 0 && next.x < n && next.y >= 0 && next.y < m && mp[next.x][next.y] != '#' && !vis[next.x][next.y]) {
vis[next.x][next.y] = true;
queue.offer(next);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
vis[i][j] = false;
}
}
while (!queue.isEmpty()) queue.poll();
now = new Point(mx, my, 0);
queue.offer(now);
vis[yx][yy] = true;
while (!queue.isEmpty()) {
now = queue.peek();
queue.poll();
if (mp[now.x][now.y] == '@'){
KFC[now.x][now.y] += now.step * 11;
ans = Math.min(KFC[now.x][now.y], ans);
}
for (int k = 0; k < 4; k++) {
Point next = new Point(now);
next.step++;
next.x += dx[k];
next.y += dy[k];
if (next.x >= 0 && next.x < n && next.y >= 0 && next.y < m && mp[next.x][next.y] != '#' && !vis[next.x][next.y]) {
vis[next.x][next.y] = true;
queue.offer(next);
}
}
}
System.out.println(ans);
}
}
}

浙公网安备 33010602011771号