CodeForces 18 E.Flag 2(dp)

本文介绍了一种针对矩阵染色问题的优化算法,旨在通过最少的步骤改变矩阵中某些位置的颜色,以达到每一行最多使用两种颜色且相邻位置颜色不相同的目标。该算法通过动态规划实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一个n×m的矩阵,每个位置可以染一种颜色,颜色共16种,用a~z表示,先给出初始染色状态,问至少要改变多少个位置的颜色才能使得满足以下两个条件:相邻两个格子颜色不同,一行至多两种不同的颜色

Input

第一行两个整数n,m表示矩阵行列数,之后输入一个n×m的字符矩阵表示初始染色状态(1n,m500)

Output

输出使得矩阵满足条件所需修改的最少颜色数和修改后的矩阵染色状态,多组解输出任意一组

Sample Input

3 4
aaaa
bbbb
cccc

Sample Output

6
abab
baba
acac

Solution

由于一行至多两种颜色且相邻两个格子颜色不同,所以让m>1时,一行的状态必然是xyxy...,其中x,y表示两种不同的颜色,以cost[i][x][y]表示把第i行变成xyxy...所需修改的次数,以dp[i][x][y]表示把第i行变成xyxy...且前i行中任意两个相邻位置颜色不同且前i行每行两种颜色所需修改的最少次数,那么有转移

dp[i+1][xx][yy]=min(dp[i+1][xx][yy],dp[i][x][y]+cost[i+1][xx][yy]),xxx,yyy,xy,xxyy

答案即为min(dp[n][x][y],xy),具体方案看当前状态是由哪个状态转移过来即可得到,时间复杂度O(264n)

m=1时,以dp[i][x]表示把第i行变成x且前i行相邻两行颜色不同所需的最少操作数,类似的有转移

dp[i+1][y]=min(dp[i+1][y],dp[i][x]+(a[i+1]y)),xy,其中a[i+1]表示第i+1行初始颜色

答案即为min(dp[n][x]),具体方案同理,时间复杂度O(262n)

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=505;
int n,m,a[maxn][maxn],f[maxn][26],dp[maxn][26][26],cost[maxn][26][26];
char s[maxn];
void output0(int pos,int x)
{
    if(pos>1)
    {
        for(int y=0;y<26;y++)
            if(x!=y&&(f[pos-1][y]+(a[pos][1]!=x)==f[pos][x]))
            {
                output0(pos-1,y);
                break;
            }       
    }
    printf("%c\n",x+'a'); 
}
void output(int pos,int x,int y)
{
    if(pos>1)
    {
        int flag=0;
        for(int xx=0;xx<26;xx++)
            if(x!=xx)
            {
                for(int yy=0;yy<26;yy++)
                    if(y!=yy&&xx!=yy)
                    {
                        if(dp[pos-1][xx][yy]+cost[pos][x][y]==dp[pos][x][y])
                        {
                            output(pos-1,xx,yy);
                            flag=1;
                            break;
                        }
                    }
                if(flag)break;
            }
    }
    for(int i=1;i<=m;i++)
        if(i&1)printf("%c",x+'a');
        else printf("%c",y+'a');
    printf("\n");
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)a[i][j]=s[j]-'a';
    }
    if(m==1)
    {
        memset(f,INF,sizeof(f));
        for(int x=0;x<26;x++)
            if(x!=a[1][1])f[1][x]=1;
            else f[1][x]=0;
        for(int i=1;i<n;i++)
            for(int x=0;x<26;x++)
                for(int y=0;y<26;y++)
                    if(x!=y)
                        f[i+1][y]=min(f[i+1][y],f[i][x]+(a[i+1][1]!=y));
        int ans=INF,x0;
        for(int x=0;x<26;x++)
            if(f[n][x]<ans)
                ans=f[n][x],x0=x;
        printf("%d\n",ans);
        output0(n,x0);
    }
    else
    {
        for(int i=1;i<=n;i++)
            for(int x=0;x<26;x++)
                for(int y=0;y<26;y++)
                    if(x!=y)
                        for(int j=1;j<=m;j++)
                            if((j&1)&&a[i][j]!=x||!(j&1)&&a[i][j]!=y)
                                cost[i][x][y]++;
        memset(dp,INF,sizeof(dp));
        for(int x=0;x<26;x++)
            for(int y=0;y<26;y++)
                dp[1][x][y]=cost[1][x][y];
        for(int i=1;i<n;i++)
            for(int x=0;x<26;x++)
                for(int y=0;y<26;y++)
                    if(x!=y)
                        for(int xx=0;xx<26;xx++)
                            if(x!=xx)
                                for(int yy=0;yy<26;yy++)
                                    if(y!=yy&&xx!=yy)
                                        dp[i+1][xx][yy]=min(dp[i+1][xx][yy],dp[i][x][y]+cost[i+1][xx][yy]);
        int ans=INF,x0,y0;
        for(int x=0;x<26;x++)
            for(int y=0;y<26;y++)
                if(x!=y&&dp[n][x][y]<ans)
                    ans=dp[n][x][y],x0=x,y0=y;
        printf("%d\n",ans);
        output(n,x0,y0);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值