牛客算法竞赛入门课第五节习题dfs/bfs
Jelly
https://ac.nowcoder.com/acm/problem/201613
https://ac.nowcoder.com/acm/problem/201613
##三维bfs
#include <bits/stdc++.h>
#include <iostream>
const int Max = 100 * 100 * 100 + 5;
#define LL long long
const int mod = 1e9+7;
//const int inx = INT_MAX;
using namespace std;
// srand(time(NULL));
// m = rand() % 1000 + 1;
//定义i i(A) i+n i+n(B) i+n+n i+n+n(C)
//bitset<Max>s,q;
typedef struct s{
int x,y,z;
}s;
queue<s>q;
int dir[105][105][105];
char ch[105][105][105];
int dis[6][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
int n;
bool check(int x,int y,int z)
{
if(x < 1 || y < 1 || z < 1 || x > n || y > n || z > n || dir[x][y][z] != 0 || ch[x][y][z] == '*')
return false;
else return true;
}
void bfs()
{
dir[1][1][1] = 1;
int x1,y1,z1;
s t;
t.x = 1;
t.y = 1;
t.z = 1;
q.push(t);
while(!q.empty()){
t = q.front();
q.pop();
for(int i = 0; i < 6; i++){
x1 = t.x + dis[i][0];
y1 = t.y + dis[i][1];
z1 = t.z + dis[i][2];
if(check(x1,y1,z1)){
dir[x1][y1][z1] = dir[t.x][t.y][t.z] + 1;
q.push((s){x1,y1,z1});
}
}
}
}
int main()
{
int i,j,k;
cin >> n;
getchar();
for(i = 1; i <= n; i++){
for(j = 1; j <= n; j++){
for(k = 1; k <= n; k++){
cin >> ch[i][j][k];
}
}
}
bfs();
if(ch[n][n][n] == '*') cout << -1;
else cout << dir[n][n][n];
return 0;
}
幸运数字
https://ac.nowcoder.com/acm/problem/15291
思路:先用dfs分别找出以4开头的和以7开头的数字存放到数组里面,又因为next[x]表示大于等于x的数字,而x,x+1,x+2...直到下一个数字之间的和是可以直接算到的,sum=(a[i] - a[i-1] + 1)*a[i].注意l和r同时小于第一个大于等于l的数。
#include <bits/stdc++.h>
#include
#include
#include <stdio.h>
const int Max = 100 * 100 * 100 + 5;
#define LL long long
LL mod = 1e9+7;
const int INF = 1e5;
//const int inx = INT_MAX;
using namespace std;
// srand(time(NULL));
// m = rand() % 1000 + 1;
//定义i i(A) i+n i+n(B) i+n+n i+n+n(C)
//bitsets,q;
//int a[100],n;
LL nex[100005],t = 2;
void dfs(LL n)
{
if(n > 1e9) return;
if(n > 10) nex[t++] = n;
dfs(n10+4);
dfs(n10+7);
}
int main()
{
nex[0] = 4,nex[1] = 7;
dfs(4);
dfs(7);
nex[t] = 4444444444;
sort(nex,nex+t+1);
LL l,r,xb;
cin >> l >> r;
LL sum = 0,s,i;
for(i = 0; i <= t; i++){
if(nex[i] >= l){
xb = i;
break;
}
}
if(r <= nex[xb]){
sum += (r - l + 1) * nex[xb];
}
else{
for(i = xb; i <= t; i++){
if(i == xb){
sum += (nex[xb] - l + 1) * nex[i];
}
else if(nex[i] > r){
sum += (r - nex[i - 1]) * nex[i];
break;
}
else{
sum += (nex[i] - nex[i - 1]) * nex[i];
}
}
}
cout << sum;
return 0;
}
递归打印全排列和集合的所有子集
全排列
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
using namespace std;
//void print(char a[])
//{
// printf("%d %c\n",strlen(a),a[2]);
//}
int a[5] = {1,2,3,4,5};
void Perm(int d,int n)
{
if(d == n){
for(int i = 0; i < 5; i++){
cout << a[i] << " ";
}
cout << endl;
return;
}
for(int i = d; i <= n; i++){
swap(a[d],a[i]);//将d个数依次与后面的数换
Perm(d+1,n);//求d+1 - n的全排列。
swap(a[d],a[i]);
}
}
int main()
{
int n,x,y,v,t;
Perm(0,4);
return 0;
} 集合的子集
递归法
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
using namespace std;
//选2时 选3 即为2 3, 不选2 即为 2.
bool a[10];
void dfs(int n,int num)
{
if(num > n){
for(int i = 1; i <= n; i++) if(a[i] == true) cout << i << " ";
cout << endl;
return;
}
a[num] = true;//选第num个数
dfs(n,num+1);
a[num] = false;//不选第num个数
dfs(n,num+1);
}
int main()
{
int n,x,y,v,t;
scanf("%d",&n);
dfs(n,1);
return 0;
}
二进制枚举法:
长度为n的集合的子集有2的n次方个子集。
题目:巅峰赛第11场 https://ac.nowcoder.com/acm/contest/6912/B
/**
* struct Point {
* int x;
* int y;
* };
*/
class Solution {
public:
/**
* 返回新集合的种类数
* @param n int整型 集合大小
* @param m int整型 限制数量
* @param limit Point类vector 不能同时出现的(u,v)集合
* @return int整型
*/
int a[25];
int cal(int n,int m,vector<Point>& limit){
int ans = 0;
for(int i = 0; i < (1 << n); i++){
memset(a,0,sizeof(a));
for(int j = 0; j < n; j++){
if(i & (1 << j)) a[j+1] = 1;
}
int cnt = 0,u,v;
for(int k = 0; k < m; k++){
u = limit[k].x;
v = limit[k].y;
if(a[u] && a[v]){
cnt++;
break;
}
}
if(!cnt) ans++;
}
return ans;
}
int solve(int n, int m, vector<Point>& limit) {
// write code here
int ans = cal(n,m,limit);
return ans;
}
}; 第九届蓝桥杯国赛-调手表:bfs
思路:相当于一个圈,每个一个单位长度有一个点,每次可以一次走1步走,或者一次走k长步,走过的点不能再走,问到每个点的步数最多是多少步。
用bfs很巧妙的可以解决。到达某个点,下一步可以走一步,或k长步,这不就是相当于在矩阵中可以往上下左右四个方向走类似吗?而我们又知道不考虑距离,只考虑一步长度的话,用bfs可以找出到每个点的最短路,先到就是最短。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#include <list>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
const ll ds = 1e15+7;
//const double p = 3.141592653589793238462643383;
const ll res = 233;
using namespace std;
int num[100005];
int main()
{
int n,k;
cin >> n >> k;
memset(num,-1,sizeof num);
num[0] = 0;
queue<int>q;
q.push(0);
while(!q.empty()){
int s = q.front();
q.pop();
if(num[(s+1)%n] == -1){
num[(s+1)%n] = num[s]+1;
q.push((s+1)%n);
}
if(num[(s+k)%n] == -1){
num[(s+k)%n] = num[s]+1;
q.push((s+k)%n);
}
}
int ans = 0;
for(int i = 0; i < n; i++){
ans = max(ans,num[i]);
}
cout << ans;
return 0;
} 钥匙:记忆化搜索
思路:直接dfs肯定不行,所有采用记忆化搜索,dp[i][j][k]表示第i个槽的前j个槽深序列为sum的二进制。但如果是用dp[pre][cur][l],第l个的前一个是pre,第l个是cur,来保存好像答案不对,也不知道为什么......。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#include <list>
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const ll ds = 1e15+7;
const double p = 3.141592653589793238462643383;
const ll res = 233;
using namespace std;
ll ans;
int a[55],n,j;
set<int>s;
ll dp[35][35][155];
int pd(int x)
{
int cnt = 0;
while(x){
if(x & 1) cnt++;
x >>= 1;
}
return cnt;
}
ll dfs(int l,int pre,int sum)
{
if(l >= n){
if(pd(sum) >= 3){
return 1;
}
return 0;
}
if(dp[l][pre][sum] != -1) return dp[l][pre][sum];
ll ans = 0;
for(int i = 1; i <= 6; i++){
if(l && abs(pre-i) >= 5) continue;
if((sum >> i) & 1) ans += dfs(l+1,i,sum);
else ans += dfs(l+1,i,sum+(1<<i));
}
dp[l][pre][sum] = ans;
return ans;
}
int main()
{
while(cin >> n){
memset(dp,-1,sizeof(dp));
cout << dfs(0,0,0) << endl;
}
return 0;
} 逃出机房
https://ac.nowcoder.com/acm/contest/10293/A 思路:如果zyj到达某个出口的时间比hl短,那么就可以逃出,bfs求出出口到两个人的最短距离即可。
#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset>
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;
const ull res = 233;
using namespace std;
struct node{
int x,y,len;
node(int a,int b,int c){
x = a;
y = b;
len = c;
}
};
char a[15][15];
int d[15][15],vis[15][15];
int z1,z2,h1,h2,n,m;
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
bool check(int x,int y)
{
if(x <= 0 || y > m || x > n || y <= 0) return false;
return true;
}
bool bfs(int x,int y)
{
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<node>q;
q.push(node(x,y,1));
vis[x][y] = 1;
d[x][y] = 0;
int h,z;
h = z = 0;
while(!q.empty()){
node s = q.front();
q.pop();
vis[s.x][s.y] = 1;
if(s.x == z1 && s.y == z2) z = s.len;
if(s.x == h1 && s.y == h2) h = s.len;
// cout << 1 << endl;
for(int i = 0; i < 4; i++){
int x1,y1;
// cout << i << endl;
x1 = s.x + dir[i][0];
y1 = s.y + dir[i][1];
if(!check(x1,y1) || vis[x1][y1] || a[x1][y1] == '#') continue;
q.push(node(x1,y1,s.len+1));
}
}
return z < h;
}
int main()
{
memset(d,0,sizeof(d));
cin >> n >> m;
getchar();
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> a[i][j];
if(a[i][j] == 'Z') z1 = i,z2 = j;
if(a[i][j] == 'H') h1 = i,h2 = j;
}
}
int flag = 0;
for(int i = 1; i<= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] == '@'){
if(bfs(i,j)) flag = 1;
}
}
}
if(flag) printf("give me northeast chicken rice and milk tea TOMORROW!");
else printf("give me northeast chicken rice and milk tea!");
return 0;
}
