• [kuangbin带你飞]专题一 简单搜索 题解


    A - 棋盘问题 POJ 1321

    一个不规则的棋盘,‘#’区域才可以摆放旗子,且同行同列只能有一个棋子。输出k个棋子的所有摆放方案。

    思路:

    ​遍历每一行每一列,放棋子加标记dfs即可。

    int n,k,ans;
    string s[10];
    bool column[10];
    void dfs(int r,int k){//k 当前剩余棋子数 
    	if(k == 0){
    		++ans;
    		return;
    	}
    	if(r == n) return;
    	dfs(r + 1,k);
    	for(int j = 0; j < n; j++){
    		if(s[r][j] == '#' && !column[j]){
    			column[j] = true;
    			dfs(r + 1, k - 1);
    			column[j] = false;//不放
    		}
    	}
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	while(cin >> n >> k){
    		if(n == -1 && k == -1) break;
    		for(int i = 0; i < 8; i++) column[i] = false;
    		ans = 0; 
    		memset(column,false,sizeof column);
    		for(int i = 0; i < n; i++) cin >> s[i];
    		dfs(0,k);
    		cout << ans << endl;
    	}
    	return 0;
    }
    
    B - Dungeon Master POJ 2251

    给你一个3维的迷宫,'#'为岩石不能通过,'.'可以通过。起点'S',终点'E'。每走一步耗时为1,询问到达重点的最短时间。直接bfs即可,字符数组g(z,x,y):第z层,x行,y列。在每一个位置可以前后左右上下移动。

    string s[35][35];
    bool flag,vis[31][31][31];
    struct node{
    	int z,x,y,time;
    }cur,top;
    int a,b,c,sx,sy,sz,dir[6][3] = {1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1};
    bool check(node cur){
    	if(vis[cur.z][cur.x][cur.y]) return false;
    	if(cur.z < 0 || cur.x < 0 || cur.y < 0 || cur.z >= c || cur.x >= a || cur.y >= b) return false;
    	return true;
    }
    void bfs(){
    	cur.z = sz,cur.x = sx,cur.y = sy,cur.time = 0;
    	queue<node> q;
    	q.push(cur);
    	while(!q.empty()){
    		top = q.front();
    		q.pop();
    		cur.time = top.time + 1;
    		for(int i = 0; i < 6; i++){
    			cur.z = top.z + dir[i][0];
    			cur.x = top.x + dir[i][1];
    			cur.y = top.y + dir[i][2];
    			if(check(cur) && s[cur.z][cur.x][cur.y] != '#'){
    				if(s[cur.z][cur.x][cur.y] == 'E'){
    					cout << "Escaped in " << cur.time << " minute(s)." << endl;
    					return;
    				}
    				vis[cur.z][cur.x][cur.y] = true;
    				q.push(cur);
    			}
    		}
    	}
    	cout << "Trapped!" << endl; 
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	while(cin >> c >> a >> b && (a + b + c)){
    		flag = true;
    		memset(vis,false,sizeof vis);
    		for(int i = 0; i < c; i++){
    			for(int j = 0; j < a; j++){
    				cin >> s[i][j];
    				for(int k = 0; k < b; k++){
    					if(s[i][j][k] == 'S') sz = i,sx = j,sy = k;
    				}
    			}
    		}
    		bfs();
    	}
    	return 0;
    }
    
    C - Catch That Cow POJ 3278

    John初始位于n位置,牛位于k位置。牛的位置不变,john有两种移动方式:

    1. 移动到相邻位置 x + 1 or - 1
    2. 移动到2 * x位置

    找到牛需要的最少移动次数?若牛的位置在人之前,则answer = n - k。其他情况,直接bfs,到达每一个节点,先判断节点是否在[0,100000]中,再判断是否到达过。

    • DP做法:
      • dp(i)表示n移动到n~k间第i个位置所需的最小移动次数
      • dp(i) = abs(i - n),(n >= k,不需要计算)
      • dp[i] 可以通过dp[i - 1],dp[i + 1]得到
      • 第二种移动为2 * i,i 奇偶分开讨论
      • i is odd:dp[i] = min(min(dp[i],dp[i - 1] + 1),min(dp[i - 1 >> 1] + 2,dp[i + 1 >> 1] + 2))
      • i is even:dp[i] = min(min(dp[i],dp[i -1] + 1),min(dp[i >> 1] + 1,dp[i >> 1 + 1] + 2))
    int DP(){
    	for(int i = 0; i <= k; i++) dp[i] = abs(n - i);//n点一步步移动到i 
    	for(int i = n + 1; i <= k; i++){
    		dp[i] = min(dp[i],dp[i - 1] + 1);
    		if(i & 1){
    			dp[i] = min(dp[i],dp[i / 2] + 2);
    			dp[i] = min(dp[i],dp[i / 2 + 1] + 2);
    		}else{
    			dp[i] = min(dp[i],dp[i / 2] + 1);
    			dp[i] = min(dp[i],dp[i / 2 + 1] + 2);			
    		}
    	} 
    	return dp[k];
    }
    
    D - Fliptile

    类似于关灯游戏,对第一行的每一个点,都有关 or 开两种选择,首先枚举第一行的各个状态,之后搜索其他行:上一行中开灯位置下一行必须翻转 -- 所有行翻转完毕后,判断最后一行是否全零。字典序最小,左上-- 右下枚举,靠近右下最优。

    const int kN = 17,inf = 0x3f3f3f3f;
    int n,m,res,g[kN][kN],book[kN][kN],mid[kN][kN],ans[kN][kN],dir[2][5] = {1,-1,0,0,0,0,0,1,-1,0};
    //g数组,记录当前的各点状态,book,存储各点初始状态。
    //mid数组,记录当前各点翻转情况,ans数组,存储当前最优翻转情况
    bool check(int x,int y){
    	if(x < 0 || y < 0 || x >= n || y >= m) return false;
    	return true;
    }
    void turn(int x,int y){
    	for(int i = 0; i < 5; i++){
    		int tx = x + dir[0][i];
    		int ty = y + dir[1][i];
    		if(check(tx,ty)) g[tx][ty] ^= 1;
    	}
    }
    void dfs_b(int r,int sum){//其它行
    	if(r == n){//所有行已翻转完毕
    		for(int i = 0; i < m; i++){
    			if(g[r - 1][i]) return;
    		}
    		if(sum <= res){
    			res = sum;
    			memcpy(ans,mid,sizeof mid);
    		}
    		return;
    	}else{
    		for(int i = 0; i < m; i++){
    			if(g[r - 1][i]){//上一行决定翻转位置 
    				++sum;
    				mid[r][i] = 1;
    				turn(r,i);
    			}else mid[r][i] = 0;
    		}
    		dfs_b(r + 1,sum);//下一行 
    	}
    } 
    void dfs_a(int l,int sum){//列 翻转个数 
    	if(l == m){
    		for(int i = 0; i < m; i++)
    			if(mid[0][i]) turn(0,i);
    		dfs_b(1,sum);//其他行 
    		memcpy(g,book,sizeof book);//恢复g数组 
    		return; 
    	}else{
    		mid[0][l] = 1;//turn
    		dfs_a(l + 1,sum + 1);
    		mid[0][l] = 0;//unturn
    		dfs_a(l + 1,sum);
    	}
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cin >> n >> m;
    	for(int i = 0; i < n; i++){
    		for(int j = 0; j < m; j++) cin >> book[i][j];
    	}
    	res = inf;
    	memcpy(g,book,sizeof book);
    	dfs_a(0,0);
    	if(res == inf) cout << "IMPOSSIBLE" << endl;
    	else{
    		for(int i = 0; i < n; i++){
    			for(int j = 0; j < m; j++){
    				if(j) cout << ' ';
    				cout << ans[i][j];
    			}
    			cout << endl;
    		}
    	} 
    	return 0;
    } 
    
    E - Find The Multiple

    输入一个n,输出由0,1组成的m为n的倍数,直接遍历所有可能...

    long long bfs(){
    	queue<long long> q;
    	q.push(1);
    	while(!q.empty()){
    		long long t = q.front();
    		q.pop();
    		if(!(t % n)) return t;
    		q.push(t * 10);
    		q.push(t * 10 + 1);
    	}
    }
    
    F - Prime Path

    找到任意两素数a,b间的最便宜的素数路径:a -->b,每次只能够变换某一位上的数字,且变换的数字也是素数,求最小变换次数。

    • 先使用素数筛求出所有的四位素数
    • bfs,模拟修改。
    int t,a,b;
    bool prime[10005],vis[10005];
    struct node{
    	int x,t;
    }top;
    void init(){//将10000以内的素数标记出来 false 表示是素数 
    	prime[1] = true;
    	for(int i = 2; i <= 10000; i++){
    		if(!prime[i]){
    			for(int j = 2 * i; j <= 10000; j += i) prime[j] = true;
    		}
    	}
    }
    void bfs(){
    	queue<node> q;
    	q.push({a,0});
    	vis[a] = true;
    	while(!q.empty()){
    		top = q.front();
    		q.pop();
    		if(top.x == b){
    			cout << top.t << endl;
    			return;
    		}
    		for(int i = 0; i < 4; i++){
    			for(int j = 0; j < 10; j++){
    				if(i == 3 && !j) continue;//最高位不可为0
    				int t =  top.x - (top.x / int(pow(10,i)) % 10) * pow(10,i) + j * pow(10,i);
    				if(!prime[t] && !vis[t]){
    					vis[t] = true;
    					q.push({t,top.t + 1});
    				}
    			}
    		}
    	}
    	cout << "Impossible" << endl;
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cin >> t;
    	init(); 
    	while(t--){
    		memset(vis,false,sizseof(vis));
    		cin >> a >> b;
    		bfs();
    	}
    	return 0;
    } 
    
    G - Shuffle'm Up

    两个字符串s1,s2,均含有c个字符。s1,s2交叉组成s12,s12尾部的c个字符为s1,其它为s2,循环操作。输入s1,s2,最终状态s12。询问结果多少次操作可以得到s12。。。

    • 模拟......,记录每次模拟的s12,注意不要重复操作
    H - Pots

    两个瓶子,量出 c L水需要的最少操作数。

    操作:
    1. fill(i),倒满i
    2. drop(i),全部倒掉i中的水
    3. pour(i,j),将i中的水倒入j中

    思路:

    1.两个杯子之间互倒(两种)

    2.每个杯子可以倒满后者全部倒掉

    3.由于需要输出最终操作步骤,在结构体中增加一个数组存储每次的操作

    4.跑一边bfs...

    //代码大致思路同M - 非常可乐
    node.turn = top.turn+1;
    if(top.a < a){//fill 
    	node.a = a,node.b = top.b;
    	if(!vis[node.a][node.b]){
    		node.ans = top.ans;
    		node.ans.push_back("FILL(1)");
    		q.push(node);
    		vis[node.a][node.b] = true;
    	}
    }	
    
    I - Fire Game

    输入n*m的矩阵,'#'表示草地,可燃烧。'.'表示空地,不可燃烧.询问点燃哪两块草地,可以是所有的草地最快燃烧。

    • 对任意一块草地,bfs,记录到达各点的时间,记录最后一层燃烧的任意一个草地x
      • 若bfs结束后存在未燃烧草地,则直接计算未燃烧草地燃烧用时
    • x bfs,记录时间,当到达某点时用时更小则修改这个点,维护一个最大时间。
    J - Fire!

    J点位人所在初始位置,F点位各个着火点,每个着火点bfs,记录其他可燃烧点的最快燃烧时间,最后j点bfs,判断是否存在到达边界时未燃烧的点。

    K - 迷宫问题

    定义一个5X5的二维数组:它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

    要求输出左上角到右下角的最短路径。

    • bfs并记录每一步的上一个位置(模拟链表思想),最后通过栈输出路径...
    //bfs模板题 记录各自父节点 找到答案 父节点入栈 --> 输出 
    int g[6][6],dir[2][4] = {-1,1,0,0,0,0,-1,1};
    bool vis[6][6];
    struct node{
    	int x,y;
    }cur,top,record[6][6];
    bool judge(int x,int y){
    	if(g[x][y] || x < 0 || x >= 5 || y < 0 || y >= 5) return false; 
    	return true;
    }
    void bfs(){
    	queue<node> q;
    	q.push({0,0});
    	vis[0][0] = true;
    	while(!q.empty()){
    		top = q.front();
    		q.pop();
    		if(top.x == 4 && top.y == 4){//输出结果 
    			stack<node> s;
    			cur = top;
    			while(cur.x || cur.y){
    				s.push(cur);
    				cur = record[cur.x][cur.y];
    			}
    			cout << "(0, 0)" << endl;
    			while(!s.empty()){
    				cur = s.top();
    				s.pop();
    				cout << '(' << cur.x << ", " << cur.y << ')' << endl;
    			}
    			return;
    		}
    		for(int i = 0; i < 4; i++){
    			cur.x = top.x + dir[0][i];
    			cur.y = top.y + dir[1][i];
    			if(judge(cur.x,cur.y) && !vis[cur.x][cur.y]){
    				vis[cur.x][cur.y] = true;
    				q.push(cur);
    				record[cur.x][cur.y] = top;
    			}
    		} 
    	}
    }
    int main(void){
    	ios_base::sync_with_stdio(0);
    	cin.tie(0);cout.tie(0);
    	for(int i = 0; i < 5; i++){
    		for(int j = 0; j < 5; j++) cin >> g[i][j];
    	}
    	bfs();
    	return 0;
    } 
    
    L - Oil Deposits

    '@ 表示石油,每找到一个@ - bfs标记相连的@,计算bfs次数。

    int ans,n,m,dir[3] = {0,-1,1};
    string g[105];
    bool vis[105][105];
    struct node{
    	int x,y;
    }cur,top;
    bool judge(node cur){
    	if(cur.x < 0 || cur.y < 0 || cur.x >= n || cur.y >= m) return false;
    	if(g[cur.x][cur.y] == '*') return false;
    	return true;
    }
    void bfs(int x,int y){
    	queue<node> q;
    	q.push({x,y});
    	vis[x][y] = true;
    	while(!q.empty()){
    		top = q.front();
    		q.pop();
    		for(int i = 0; i < 3; i++){
    			for(int j = 0; j < 3; j++){
    				cur.x = top.x + dir[i];
    				cur.y = top.y + dir[j];
    				if(judge(cur) && !vis[cur.x][cur.y] && (i | j)){
    					vis[cur.x][cur.y] = true;
    					q.push(cur);
    				}
    			}
    		}
    	}
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	while(cin >> n >> m){
    		if(!n && !m) break;
    		memset(vis, false, sizeof vis);
    		ans = 0;
    		for(int i = 0; i < n; i++) cin >> g[i];
    		for(int i = 0; i < n; i++){
    			for(int j = 0; j < m; j++){
    				if(g[i][j] == '@' && !vis[i][j]){
    					bfs(i,j);
    					++ans;
    				}
    			}
    		}
    		cout << ans << endl;
    	}
    	return 0;
    }
    
    M - 非常可乐

    大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

    • s需要均分,必须为偶数

    • 3个杯子互相倒,每种状态有六种可能的倒法:n->m n->s m->n m->s s->n s->m,bfs。。。

    //s->n n未满 s有剩余 
    if(top.s && top.n<n){
    	//s剩余量可不可以使n填满 
    	if(top.s >= n-top.n) node.n = n,node.s = top.s-n+top.n;
    	else node.n = top.n+top.s,node.s = 0; 
    	node.m = top.m;
    	node.time = top.time+1;
    	//每种状态入队一次 
    	if(!vis[node.n][node.m]){ 
    		q.push(node);
    		vis[node.n][node.m] = true;
    	} 
    }
    
    • 数学做法:...
    N - Find a way

    Y和M要在‘@’见面,每走一步耗时十一分钟,初始时y m位于两个位置。两人要去的‘@’总耗时最小。

    • ‘@’有多个,对每一个跑一遍bfs()求最小总步数会超时....

    • y,m各自bfs(),记录到达各个‘@’的时间,寻找到达‘@’的步数总和最小的点...

    const int INF = 0x3f3f3f3f;
    int n,m,ans,book[2][210][210],dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
    struct node{
    	int x,y,step;
    }cur,top;
    vector<pair<int,int>> posi;
    string g[205];
    bool vis[205][205]; 
    bool judge(int x, int y){
    	if(x < 0 || x >= n || y < 0 || y >= m) return false;
    	if(g[x][y] == '#') return false;
    	return true;
    }
    void bfs(int f, int x, int y){
    	memset(vis,false,sizeof(vis));
    	vis[x][y] = true;
    	queue<node> q;
    	q.push({x,y,0});
    	while(!q.empty()){
    		top = q.front();
    		q.pop();
    		cur.step = top.step+1;
    		if(g[top.x][top.y] == '@'){//'@'所在位置记录一遍 
    			book[f][top.x][top.y] = top.step;
    			if(f) posi.push_back(make_pair(top.x,top.y));	
    		}
    		for(int i = 0; i < 4; i++){
    			cur.x = top.x + dir[i][0];
    			cur.y = top.y + dir[i][1];
    			if(judge(cur.x, cur.y) && !vis[cur.x][cur.y]){
    				vis[cur.x][cur.y] = true;
    				q.push(cur);
    			}
    		}
    	} 
    }
    
    int main(void){
    	ios_base::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	while(cin >> n >> m){
    		for(int i=0;i<n;i++) cin >> g[i];
    		for(int i = 0; i < n; i++){
    			for(int j = 0; j < m; j++){
    				if(g[i][j] == 'Y') bfs(1,i,j); 
    				if(g[i][j] == 'M') bfs(0,i,j);
    			}
    		}
    		ans = INF;
     		for(int i = 0; i < posi.size(); i++){//最小总步数
    			ans = min(ans,book[0][posi[i].first][posi[i].second] + book[1][posi[i].first][posi[i].second]);
    		} 
    		posi.clear();
    		cout << ans * 11 << endl;
    	}
    	return 0; 
    }
    
  • 相关阅读:
    struts2 局部类型转换器
    Struts2 命名空间配置
    Struts2 国际化
    Struts2 跟踪用户状态
    Struts2
    Struts2支持的处理结果类型
    SGI STL内存配置器(一):内存泄漏?
    TP 3.2 笔记 (1)
    AOP TP框架有感
    eclipse+git
  • 原文地址:https://www.cnblogs.com/honey-cat/p/12924720.html
Copyright © 2020-2023  润新知