把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BFS·思维】Pohlepko--7.2测试 COCI

样例
4 5
ponoc
ohoho
hlepo
mirko

4 5
bbbbb
bbbbb
bbabb
bbbbb

2 5
qwert
yuiop

分析

首先,不管怎么走,走出来的字符串是长度相等的,这个比较好理解
那么,字典序的比较就决定于比到的第一个不一样的字符
所以我们每次走的时候就比较一下右边和下边,走最小的那边
于是,第一个骗分程序:

//50% 
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
#define MAXN 2005
int n,m,cnt;
char s[MAXN][MAXN],ans[MAXN*2];
const int dx[]={0,1},dy[]={1,0};
bool check(int x,int y)
{
	if(x<1||x>n||y<1||y>m)
		return 0;
	return 1;
}
void dfs(int r,int c)
{
	//printf("%d %d\n",r,c);
	if(r==n&&c==m) return ;
	char tmp=123;
	int nx,ny;
	
	int x=r+dx[0],y=c+dy[0];
	if(check(x,y))
		tmp=s[x][y],nx=x,ny=y;
	//if(r==4&&c==4)  printf("%d %d\n",nx,ny);
	x=r+dx[1],y=c+dy[1];
	if(check(x,y))
	{
		if(s[x][y]<tmp)
		{
			ans[++cnt]=s[x][y];
			dfs(x,y);
			return ;
		}	
	}
	ans[++cnt]=tmp;
	dfs(nx,ny);
	return ;
}
int main()
{
	//freopen("imena.in","r",stdin);
	//freopen("imena.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]+1);
	ans[++cnt]=s[1][1];
	dfs(1,1);
	puts(ans+1);
	return 0;
}

它是不能处理相等的情况的,所以只能过 50 % 50% 50

然后,我就开始想剩下的 50 % 50% 50

1. d f s dfs dfs能不能设置一个返回值,传回来一个反馈 用这个反馈来指引走哪条路
想法很美好,但是我不会写 或许周六放假会回去试一试
就是碰到了相同的就分别都搜下去
看看搜到了些什么
直到搜到了有一个节点 它的右边跟下边不同
然后传参什么的回来就是在哪里(往哪边走)找到一个最小的(这么一想 好像跟正解有点相似

2.这么一想,也就是当前我走哪条路是要取决于后面的走法的
也就是说,我选择的路,是选右边和下边的子串最小的那条
于是我就想到了dp 从左下角推倒右上角
然后就产生了第二种50分算法:

//
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<string>
using namespace std;
#define LL long long
#define MAXN 2005
#define INF 0x3f3f3f3f
int n,m;
char s[MAXN][MAXN];
string val[MAXN][MAXN];
int main()
{
	freopen("pohlepko.in","r",stdin);
	freopen("pohlepko.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]+1);
	val[n][m]=s[n][m];
	for(int j=m-1;j>=1;j--)
		val[n][j]=s[n][j]+val[n][j+1];
	for(int i=n-1;i>=1;i--)
		val[i][m]=s[i][m]+val[i+1][m];
	for(int i=n-1;i>=1;i--)
		for(int j=m-1;j>=1;j--)
		{
			if(val[i+1][j]<val[i][j+1])
				val[i][j]=s[i][j]+val[i+1][j];
			else val[i][j]=s[i][j]+val[i][j+1];
		}
	cout<<val[1][1]<<endl;
	return 0;
}

我当时觉得这个简直就是 p e r f e c t perfect perfect
结果 M L E MLE MLE
对 我相当于存了所有的情况 字符串太长了然后就爆栈了 q w q qwq qwq


然后,就是正解:
类似于广搜
遇到相等的情况 就继续搜下去
搜索树中的同一层 找到最小的那个 我们就走最小的那个
其它的可以丢了
如果最小的有多个 就继续搜索每一个最小的

//
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<string>
#include<queue>
using namespace std;
#define LL long long
#define MAXN 2005
#define INF 0x3f
int n,m;
char s[MAXN][MAXN];
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int d[MAXN][MAXN],cnt[MAXN*2];
int w[MAXN][MAXN];//路径 
bool vis[MAXN][MAXN];
queue<int>Q1,Q2;
void bfs()
{//d是搜索树里的层数 cnt是这一层里最小的字母-'a' 
	memset(cnt,0x3f,sizeof(cnt));
	vis[1][1]=1;
	d[1][1]=1;
	cnt[1]=s[1][1]-'a';
	while(!Q1.empty()) Q1.pop();
	while(!Q2.empty()) Q2.pop();
	Q1.push(1);
	Q2.push(1);
	while(!Q1.empty())
	{
		int x=Q1.front(),y=Q2.front();
		Q1.pop();Q2.pop();
		if(s[x][y]-'a'!=cnt[d[x][y]])
			continue;
	//不等的话 就是之前比较的时候还没有比到最小值 它是之前的当前最小值 而我们只继续搜字典序最小的那一个或那几个
		for(int i=0;i<=1;i++)
		{
			int nx=x+dx[i],ny=y+dy[i];
			if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&!vis[nx][ny])
			{
				d[nx][ny]=d[x][y]+1;
				vis[nx][ny]=1;
				if(cnt[d[nx][ny]]>=s[nx][ny]-'a')
				{
					cnt[d[nx][ny]]=s[nx][ny]-'a';
					Q1.push(nx); Q2.push(ny);
					//拓展下一层节点的话 就只push字典序最小的那一个或那几个
					w[nx][ny]=i+2;//到(w1,w2)这个节点来 是上一个位置通过d[]数组i+2到的
					//实际上的路径只需要记录最小的那一条 
					//记录下来所有可能的路径(有的是当前最小 会被淘汰 整个节点都是无效的 
					
				}
			}
		}
	}
}
void print(int x,int y)
{
	//printf("%d %d\n",x,y);
	if(x==1&&y==1)
	{
		printf("%c",s[x][y]);
		return ;
	}
	print(x+dx[w[x][y]],y+dy[w[x][y]]);
	printf("%c",s[x][y]);
} 
int main()
{
	freopen("pohlepko.in","r",stdin);
	freopen("pohlepko.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]+1);
	bfs();
	print(n,m);
	return 0;
}
posted @ 2019-07-03 11:12  Starlight_Glimmer  阅读(22)  评论(0编辑  收藏  举报  来源
浏览器标题切换
浏览器标题切换end