[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 
#include 
#include 
#include 
#include 
#include 
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( xs[x][y+1] ) {//向右方移动更优 
                    if( s[x][y+1]


#include 
#include 
#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]s[x][y+1] ) Solve( x, y+1 );
                else Solve( x+1, y ), Solve( x, y+1 );
            } 
            else if( x


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



你可能感兴趣的:(贪心,考试)