目录
1、母亲的牛奶(usaco training 1.5)
2、走迷宫(模板)
3、八数码1(模板)
4、八数码2(《算法竞赛进阶指南》& HDU1043)
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 cinsert(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 cinsert(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 binsert(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++];//popfor(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;//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//千万别忘了memsetmemset(vis,-1,sizeof vis);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&g[i][j]);}//read graphbfs(); 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 08*/
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//19int 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;
}