【CodeForces - 18E】Flag 2(暴力DP+路径输出)

版权声明:本文为博主原创文章,不管你喜不喜欢都请在注明作者后转载~( ̄▽ ̄~) https://blog.csdn.net/C20190102/article/details/81775186

题目

  • 2 S
  • 128 MB

题目描述
According to a new ISO standard, a flag of every country should have, strangely enough, a chequered field n × m , each square should be wholly painted one of 26 colours. The following restrictions are set:

  • In each row at most two different colours can be used.
  • No two adjacent squares can be painted the same colour.
  • Pay attention, please, that in one column more than two different colours can be used.

Berland’s government took a decision to introduce changes into their country’s flag in accordance with the new standard, at the same time they want these changes to be minimal. By the given description of Berland’s flag you should find out the minimum amount of squares that need to be painted different colour to make the flag meet the new ISO standard. You are as well to build one of the possible variants of the new Berland’s flag.

输入
The first input line contains 2 integers n and m ( 1 n , m 500 ) — amount of rows and columns in Berland’s flag respectively. Then there follows the flag’s description: each of the following n lines contains m characters. Each character is a letter from a to z, and it stands for the colour of the corresponding square.

输出
In the first line output the minimum amount of squares that need to be repainted to make the flag meet the new ISO standard. The following n lines should contain one of the possible variants of the new flag. Don’t forget that the variant of the flag, proposed by you, should be derived from the old flag with the minimum amount of repainted squares. If the answer isn’t unique, output any.

样例
输入
3 4
aaaa
bbbb
cccc
输出
6
abab
baba
acac

输入
3 3
aba
aba
zzz
输出
4
aba
bab
zbz

题目大意

n × m 的一个棋盘上,每个格子有一个颜色(az中的一种),你可以任意置换(不是交换)其中的颜色,每置换一次的代价是 1 ,用最小的代价达到以下条件:

  • 相邻格子的颜色均不相同。
  • 每行最多只能有 2 种颜色。

输出最小代价和任意一种涂色方案。

分析

由于每行只能有两种颜色,而且相邻的颜色不同,所以每行一定是ABABA...的样子,注意ABABA...BABAB...是不同的。所以直接定义: d p [ i ] [ j ] [ k ] 表示前 i 行,第 i 行的第一个颜色为 j ,第二个颜色为 k (即jkjkj...)的最小代价,那么:

d p [ i ] [ j ] [ k ] = min 1 p , q 26 j p , k q ( d p [ i 1 ] [ p ] [ q ] ) + c o s t [ i ] [ j ] [ k ]

暂且不说 c o s t [ i ] [ j ] [ k ] ,这个式子就是:
t图示
可能你会发现,还需要满足 p q j k ,但是我们只需要把这种情况用 c o s t 数组来规避就可以了。

c o s t [ i ] [ j ] [ k ] 代表把第 i 行变成jkjkj...的形式的代价,遇到 j = k 的情况,赋成 即可,可以用 O ( n × m × 26 2 ) 初始化出来。

然后 5 层循环暴力DP即可,时间复杂度: O ( n × 26 4 ) 10 8 的复杂度,CF强大的评测机让我 1200 m s 过了。

代码

略长,还不算太丑。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 500
#define MAXM 500
#define MAXC 26
#define INF 0x3f3f3f3f
int N,M;
char color[MAXN+5][MAXM+5];
int dp[MAXN+5][MAXC+5][MAXC+5];
int cost[MAXN+5][MAXC+5][MAXC+5];
pair<int,int> pre[MAXN+5][MAXC+5][MAXC+5];

void Init(){
    //初始化dp和cost
    memset(dp,0x3f,sizeof dp);
    memset(cost,0x3f,sizeof cost);
    for(int i=1;i<=N;i++){
        for(int j=1;j<=MAXC;j++){
            for(int k=1;k<=MAXC;k++){
                if(j==k)//注意这里
                    continue;
                cost[i][j][k]=0;
                for(int p=1;p<=M;p+=2)//颜色1
                    cost[i][j][k]+=(color[i][p]!=('a'+j-1));
                for(int p=2;p<=M;p+=2)//颜色2
                    cost[i][j][k]+=(color[i][p]!=('a'+k-1));
            }
        }
    }
    //初始化第一行,否则全是INF
    for(int i=1;i<=MAXC;i++)
        for(int j=1;j<=MAXC;j++)
            dp[1][i][j]=cost[1][i][j];
}
//输出方案
void Print(int i,int color1,int color2){
    if(i>1)
        Print(i-1,pre[i][color1][color2].first,pre[i][color1][color2].second);
    for(int i=1;i<=M;i++)
        if(i&1) putchar('a'+color1-1);
        else putchar('a'+color2-1);
    putchar('\n');
}

int main(){
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++)
        scanf("%s",color[i]+1);
    Init();
    for(int i=1;i<=N;i++)
        for(int j=1;j<=MAXC;j++)
            for(int k=1;k<=MAXC;k++)
                for(int p=1;p<=MAXC;p++)
                    for(int q=1;q<=MAXC;q++)
                        if(p!=j&&q!=k)
                            if(dp[i][j][k]>dp[i-1][p][q]+cost[i][j][k]){
                                pre[i][j][k]=make_pair(p,q);
                                //pre存上一行的颜色
                                dp[i][j][k]=dp[i-1][p][q]+cost[i][j][k];
                            }
    int Ans=INF,ci,cj;
    for(int i=1;i<=MAXC;i++)
        for(int j=1;j<=MAXC;j++)
            if(Ans>dp[N][i][j])//统计答案
                Ans=dp[N][ci=i][cj=j];
    printf("%d\n",Ans);
    Print(N,ci,cj);
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/81775186