目录
5、全球变暖(第九届蓝桥杯省赛C++ & JAVA A组/B组)
1、母亲的牛奶(usaco training 1.5)
农夫约翰有三个容量分别为 A,B,C 升的挤奶桶。
最开始桶 A 和桶 B 都是空的,而桶 C 里装满了牛奶。
有时,约翰会将牛奶从一个桶倒到另一个桶中,直到被倒入牛奶的桶满了或者倒出牛奶的桶空了为止。
这一过程中间不能有任何停顿,并且不会有任何牛奶的浪费。
请你编写一个程序判断,当 A 桶是空的时候,C 桶中可能包含多少升牛奶,找出所有的可能情况。
输入格式
共一行,包含三个整数 A,B,C
输出格式
共一行,包含若干个整数,表示 C 桶中牛奶存量的所有可能情况,请将这些数字按升序排列。
数据范围
1≤A,B,C≤20
输入样例1:
8 9 10
输出样例1:
1 2 8 9 10
输入样例2:
2 5 10
输出样例2:
5 6 7 8 9 10
思路:
枚举每一种情况,存到vis[][][]数组中,最后进行遍历,把a=0的情况输出出来
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int A,B,C;//容量
int cc=C,ca=0,cb=0;//c桶7装满了奶,而a桶和b桶是空的
bool vis[N][N][N];
int hh,tt;
struct node
{
int a,b,c;
}q[N*N*N];
void insert(int a,int b,int c)
{
if(!vis[a][b][c])
{
q[++tt]={a,b,c};
vis[a][b][c]=true;
}
}
void bfs()
{
q[0]={0,0,C};
vis[0][0][C]=true;
while(hh<=tt)
{
auto t=q[hh++];
int a=t.a;
int b=t.b;
int c=t.c;
//a to b
insert(a - min(a ,B - b),min(a + b, B ), c);
//a to c
insert(a - min(a, C - c), b, min(C , c + a));
//b to a;
insert(min(A,a+b),b-min(b,A-a),c);
//b to c
insert(a,b-min(b,C-c),min(c+b,C));
//c to a;
insert(min(A,a+c),b,c-min(A-a,c));
//c to b
insert(a,min(b+c,B),c-min(B-b,c));
}
}
int main()
{
cin>>A>>B>>C;
//cout<<A<<B<<C;
bfs();
//cout<<vis[0][9][1];
for(int i=0;i<=C;i++)
for(int j=0;j<=B;j++)
if(vis[0][j][i])
{
cout<<i<<" ";
}
return 0;
}
2、走迷宫(模板)
给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。
最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角 (n,m)处,至少需要移动多少次。
数据保证 (1,1)处和 (n,m) 处的数字为 0,且一定至少存在一条通路。
输入格式
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含 m个整数(0 或 1),表示完整的二维数组迷宫。
输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。
数据范围
1≤n,m≤100
输入样例:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出样例:
8
思路:
经典的广度优先搜索模板,不过这次vis数组不仅用来记录是否访问,还用来记录步数
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=103;
int n,m;
typedef pair<int,int> PII;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int g[N][N];
int vis[N][N];
int hh=0,tt=0;
PII q[N*N];
void bfs()
{
q[0]={1,1};
while(hh<=tt)
{
PII t=q[hh++];//pop
for(int i=0;i<4;i++)
{
int nx=t.first+dx[i];
int ny=t.second+dy[i];
if(nx>=1 && ny>=1 && nx<=n && ny<=m && vis[nx][ny]==-1 && g[nx][ny]==0)
{
vis[nx][ny]=vis[t.first][t.second]+1;
q[++tt]={nx,ny};//enqueue
}
}
}
}
int main()
{
cin>>n>>m;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//千万别忘了memset
memset(vis,-1,sizeof vis);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&g[i][j]);
}//read graph
bfs();
cout<<vis[n][m]+1;
return 0;
}
/*
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
8
*/
3、八数码1(模板)
在一个 3×3的网格中,1∼8 这 8 个数字和一个 x
恰好不重不漏地分布在这 3×3的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
思路:
数组中x的位置索引k可以转化出x在二维数组中的坐标:
x=k/3,y=k%3
代码:
#include<bits/stdc++.h>
using namespace std;
string start;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
//2 3 4 1 5 x 7 6 8
//19
int bfs(string start)
{
//if(start==end)return d[] 不小心写递归了
string end="12345678x";
unordered_map<string,int>d;
d[start]=0;//初始步数为0;
queue<string>q;
q.push(start);
while(q.size())
{
string t=q.front();
q.pop();
int distance=d[t];
//int cnt1=0;
//for(int i=0;i<3;i++)
//{
//for(int j=0;j<3;j++)
//{
//cout<<t[cnt1++]<<" ";
//}
//cout<<endl;
//}
//cout<<endl;
//cout<<d[t]<<endl;
if(t==end)return distance;
int k=t.find('x');
int x=k/3;
int y=k%3;//转化出'x'的位置
for(int i=0;i<4;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
//cout<<dx[i]<<" "<<dy[i]<<endl;
//cout<<nx<<" "<<ny<<endl;
if(nx>=0 && nx<=2 && ny>=0 && ny<=2 )
{
//cout<<k<<" "<<nx*3+y<<endl;
swap(t[k],t[nx*3+ny]);
//cout<<"new"<<endl;
if(!d.count(t))
{
d[t]=distance+1;
q.push(t);
}
swap(t[k],t[nx*3+ny]);
}
}
//cout<<-1;
}
return -1;
}
int main()
{
string start="";
for(int i=0;i<9;i++)
{
char c;
cin>>c;
start+=c;
}
cout<<bfs(start);
return 0;
}
4、八数码2(《算法竞赛进阶指南》& HDU1043)
在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x
恰好不重不漏地分布在这 3×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
把 x
与上下左右方向数字交换的行动记录为 u
、d
、l
、r
。
现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。
如果答案不唯一,输出任意一种合法方案即可。
如果不存在解决方案,则输出 unsolvable
。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
ullddrurdllurdruldr
思路:
升级版的八数码1,这次新增两个表即可:用哈希表pre来记录状态转移,再开一个哈希表记录每个状态对应的步数即可
代码:
#include<bits/stdc++.h>
using namespace std;
string s;
unordered_map<string,string>pre;
unordered_map<string,char>h;
char oper[]="udlr";
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
unordered_set<string>st;
queue<string>q;
//2 3 4 1 5 x 7 6 8
//1 2 3 4 5 6 x 7 8
//ullddrurdllurdruldr
// u d l r
bool bfs()
{
string end="12345678x";
q.push(s);
st.insert(s);
while(q.size())
{
auto t=q.front();
if(t==end)return true;
q.pop();
auto cur=t;
int k=t.find('x');
int x=k/3;
int y=k%3;
for(int i=0;i<4;i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
swap(t[k],t[nx*3+ny]);
if(nx>=0 && ny>=0 && nx<=2 && ny<=2 && pre.find(t)==pre.end())
{
q.push(t);
pre[t]=cur;
h[t]=oper[i];
}
swap(t[k],t[nx*3+ny]);
}
}
return false;
}
int main()
{
string res;
for(int i=0;i<9;i++)
{
char c;
cin>>c;
s+=c;
}
if(!bfs())cout<<"unsolvable";
else
{
string t="12345678x";
while(true)
{
if(t==s)
{
break;
}
res=h[t]+res;
t=pre[t];
}
}
cout<<res;
return 0;
}
5、全球变暖(第九届蓝桥杯省赛C++ & JAVA A组/B组)
你有一张某海域 N×N 像素的照片,”.”表示海洋、”#”表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中”上下左右”四个方向上连在一起的一片陆地组成一座岛屿,例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入格式
第一行包含一个整数N。
以下 N 行 N 列,包含一个由字符”#”和”.”构成的 N×N字符矩阵,代表一张海域照片,”#”表示陆地,”.”表示海洋。
照片保证第 1 行、第 1 列、第 N 行、第 N 列的像素都是海洋。
输出格式
一个整数表示答案。
数据范围
1≤N≤1000
输入样例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出样例1:
1
输入样例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
输出样例2:
1
思路:
遍历图,遇到岛屿开始搜索,广度优先搜索"#"的上下左右是否有".",如果有则标记为淹没,如果旁边有岛屿像素则都属于一个岛,这个岛屿像素要入队,且标记为访问过("."不做处理以便于这个"."来淹没周围的岛屿),如果这个岛最后还有没有被淹没的单位则这个岛存活,最后用原来的总数减去存活岛屿数量即可
代码:
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
char arr[1010][1010];
bool finddao[1010][1010];
int N;
bool bfs(pair<int,int>s1){
int sum=0;//计算不被海水侵蚀的路地块个数
bool mark;//用于判断是否被侵蚀
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};//初始化偏移量
queue<pair<int,int>>dui;
dui.push(s1);
while(dui.size()){
auto s2=dui.front();
dui.pop();
mark=1;
for(int i=0;i<4;i++){
int x1=s2.x+dx[i],y1=s2.y+dy[i];
if(x1<0||x1>=N||y1<0||y1>=N)continue;
if(arr[x1][y1]=='.'){mark=0;continue;}
if(finddao[x1][y1])continue;
finddao[x1][y1]=true;
dui.push(make_pair(x1,y1));
}
if(mark==1)sum++;
}
if(sum>=1)//如果岛上有不被侵蚀的路地块那么这岛能存活
return 1;
else return 0;
}
int main(){
int allisland=0,lifeisland=0;
pair<int,int> s1;
cin>>N;
for(int i=0;i<N;i++)cin>>arr[i];
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(arr[i][j]=='#'&&!finddao[i][j]){
s1={i,j};
allisland++;//计算所有岛的数目
lifeisland+=bfs(s1);//计算能存活的岛
}
}
}
cout<<allisland-lifeisland;//用所有的岛数目减能留下的岛数目剩下就是沉下的岛
return 0;
}