[NOIP模拟赛]单词

【问题描述】

有一个有字母表,一共有N行M列,你从左上角开始出发,目的地是右下角。每次你只能往右或往下走一步。将你经过的格子里面的字母按照访问顺序组成一个单词。求你能得到的字典序最小的单词是什么?


【输入】
第一行包含N和M,(1<=N,M<=2000)

接下来N行,每行包含M个小写字母。


【输出】
输出最小字典序的单词。

40%的数据,每个格子的右、下的字母不同。


【输出样例】
4 5
ponoc
ohoho
hlepo

mirko


4 5
bbbbb
bbbbb
bbabb

bbbbb


2 5
qwert

yuiop


【输出样例】
pohlepko
bbbbabbb

qweiop



题解:
题目要求字典序最小,因此不能简单地把字母转换成数字进行dp求和最小的,因为ab优于ba,ae优于bc。
正解:运用贪心的思路,每次从一个位置出发,选其右方和下方小的字符,把它的x放入队列,相同则两个都放入。
从左上到右下, 一共会走n+m-1步,而对于当前步step可以到达的点,其位置一定满足x+y-1=step,这样就可以通过队列中的x确定字符位置。

提醒:最好用滚动数组模拟队列,否则可能会T。


第一个代码是顺着思路码的,虽然长但是便于理解。第二个代码是经过简化的。


#include<iostream> 
#include<cstring> 
#include<cstdio> 
#include<cstdlib> 
#include<cmath> 
#include<algorithm> 
using namespace std; 
const int N=2005; 
int n, m, q[2][N], l[2], r[2];//q: 记录x   l、r: 记录队列左右界限 
char s[N][N], note[N<<1]; 
bool p, vis[N];//p: 记录当前队列  vis: 去重 
  
  
int main() { 
    scanf( "%d%d", &n, &m ); 
    for( int i=1; i<=n; i++ ) scanf( "%s", s[i]+1 ); 
	
    note[1]=s[1][1]; 
    l[p]=1; r[p]=0; q[p][ ++r[p] ]=1; 
    int all=n+m-1; 
	
    for( int step=2; step<=all; step++) { 
		
        memset( vis, 0, sizeof vis ); 
        p^=1; l[p]=1; r[p]=0;
        char minc='z'+1;//记录当前队列的最小值 
		
        while( l[ p^1 ]<=r[ p^1] ) { 
			
            int x=q[ p^1 ][ l[ p^1 ] ]; l[ p^1 ]++; 
            int y=step-x;//确定当前位置 
			
            if( x<n && y<m ) {//可以向右方和下方移动 
				
                if( s[x+1][y]<s[x][y+1] ) {//向下方移动更优 
                    if( s[x+1][y]<minc ) l[p]=1, r[p]=0;//更新队列 
                    if( s[x+1][y]<=minc && !vis[x+1] ) { 
                        q[p][ ++r[p] ]=x+1; vis[x+1]=1; 
                        minc=s[x+1][y];                  
                    } 
                } 
				
                else if( s[x+1][y]>s[x][y+1] ) {//向右方移动更优 
                    if( s[x][y+1]<minc ) l[p]=1, r[p]=0; 
                    if( s[x][y+1]<=minc && !vis[x] ) { 
                        q[p][ ++r[p] ]=x; vis[x]=1; 
                        minc=s[x][y+1]; 
                    } 
                } 
				
                else {//相同 
                    if( s[x][y+1]<minc ) l[p]=1, r[p]=0; 
                    if( s[x][y+1]<=minc ) { 
                        if( !vis[x] ) 
                            q[p][ ++r[p] ]=x, vis[x]=1; 
                        if( !vis[x+1] ) 
                            q[p][ ++r[p] ]=x+1, vis[x+1]=1; 
                        minc=s[x][y+1]; 
                    } 
                } 
				
            } 
			
            else if( x<n ) {//只能向下方移动 
                if( s[x+1][y]<minc ) l[p]=1, r[p]=0;//更新队列 
                if( s[x+1][y]<=minc && !vis[x+1] ) { 
                    q[p][ ++r[p] ]=x+1; 
                    minc=s[x+1][y]; 
                    vis[x+1]=1; 
                } 
            } 
			
            else {//只能向右方移动 
                if( s[x][y+1]<minc ) l[p]=1, r[p]=0; 
                if( s[x][y+1]<=minc && !vis[x] ) { 
                    q[p][ ++r[p] ]=x; 
                    minc=s[x][y+1]; 
                    vis[x]=1; 
                } 
            } 
			
        } 
		
        note[step]=minc; 
    } 
	
    printf( "%s\n", note+1 ); 
    return 0; 
}


#include<cstring> 
#include<cstdio> 
#define re(p) p^1
const int N=2005; 
int n, m, q[2][N], l[2], r[2];
char s[N][N], note[N<<1], minc; 
bool p, vis[N];

void Solve( int x, int y ) {
	if( s[x][y]<minc ) l[p]=1, r[p]=0;
	if( s[x][y]<=minc && !vis[x] )
		q[p][ ++r[p] ]=x, vis[x]=1, minc=s[x][y];
}
  
int main() {
    scanf( "%d%d", &n, &m ); 
    for( int i=1; i<=n; i++ ) scanf( "%s", s[i]+1 ); 
    note[1]=s[1][1]; 
    l[p]=1; r[p]=0; q[p][ ++r[p] ]=1; 
    int all=n+m-1; p^=1;
    for( int step=2; step<=all; step++, p^=1 ) { 
        minc='z'+1; l[p]=1; r[p]=0;
        while( l[ re(p) ]<=r[ re(p) ] ) { 
            int x=q[ re(p) ][ l[ re(p) ] ], y=step-x; l[ re(p) ]++;
            if( x<n && y<m ) {
                if( s[x+1][y]<s[x][y+1] ) Solve( x+1, y );
                else if( s[x+1][y]>s[x][y+1] ) Solve( x, y+1 );
                else Solve( x+1, y ), Solve( x, y+1 );
            } 
            else if( x<n ) Solve( x+1, y );
            else Solve( x, y+1 );
        }
        note[step]=minc;
		memset( vis, 0, sizeof vis );
    }
    printf( "%s\n", note+1 ); 
    return 0; 
}


P.S.据说还有一种做法:开滚动数组char s[2][MAXN][MAXN*2]记录字符串,直接用字符串进行比较,就可以用dp了



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值