题解:freee Programming Contest 2022(AtCoder Beginner Contest 264)之F - Monochromatic Path (atcoder.jp)

题解:freee Programming Contest 2022(AtCoder Beginner Contest 264)F - Monochromatic Path (atcoder.jp)

·难度

算法难度:普及

思维难度:提高

调码难度:提高

综合评价:困难

·算法

动态规划+问题拆分

·思路

状态:f[i][j][x][y]表示考虑到第i行第j列、第i行被翻转了x次、第j列被翻转了y次。根据!的性质可以得出x、y是0、1之中的数。

初值:f[1][1][0][0]=0;f[1][1][1][0]=r[i];f[1][1][0][1]=c[j]; f[1][1][1][1]=r[i]+c[j];

转移:

                if(i!=1){
                    if(a[i][j]==a[i-1][j]){
                        f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][0][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][1][0]+r[i]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][0][1]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][1][1]+r[i]);
                    }else{
                        f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][1][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][0][0]+r[i]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][1][1]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][0][1]+r[i]);
                    }
                }
                if(j!=1){
                    if(a[i][j]==a[i][j-1]){
                        f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][0]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][1]+c[j]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][1]+c[j]);
                    }else{
                        f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][1]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][1]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][0]+c[j]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][0]+c[j]);
                    }
                }

原理自己理解……

好吧我说一说:

其实对于四种i、j的子状态的原理类似,就是如果上面有元素就用上面的元素转移它,左侧有元素就有左侧的元素转移它,如果都有就分别转移。转移分为两大类:被转移元素颜色与转移元素颜色相同时,需要让两元素所在行或列翻转次数除以2的余数相同,若颜色不同则让余数差1。需要注意的有两点:每次转移时需要将本次翻转的代价加上;被转移元素和转移元素的翻转操作可能重合(比方说在同一行的两个元素共用一个行翻转操作),且不能有矛盾(比方说被转移元素状态为i、j、1、1,而我们却用i、j-1、0、0【在被转移元素左侧】转移它肯定不行,因为我们无论是对于被转移元素还是转移元素,实际上都是做了换行操作的,因此不应该是{0,0},而应该是{1,0})。

好难……(这题是给人出的吗

·代码

AC 提交记录

#include<bits/stdc++.h>
#define M 2200
using namespace std;
long long f[M][M][2][2]={};
int a[M][M]={},c[M]={},r[M]={},h=0,w=0;
int main(){
    scanf("%d%d",&h,&w);
    for(int i=1;i<=h;i++){
        scanf("%d",&r[i]);
    }
    for(int i=1;i<=w;i++){
        scanf("%d",&c[i]);
    }
    for(int i=1;i<=h;i++){
        string str="";
        cin>>str;
        for(int j=1;j<=w;j++){
            a[i][j]=str[j-1]-'0';
        }
        //注意:这里由于中间没有空格,需要用字符串输入,而字符串是从0开始的
    }
    for(int i=1;i<=h;i++){
        for(int j=1;j<=w;j++){
            if(i==1&&j==1){
                f[i][j][0][0]=0;
                f[i][j][1][0]=r[i];
                f[i][j][0][1]=c[j];
                f[i][j][1][1]=r[i]+c[j];
                //对于{1,1,x,y}进行初始化
            }else{
                f[i][j][0][0]=1LL<<60;
                f[i][j][1][0]=1LL<<60;
                f[i][j][0][1]=1LL<<60;
                f[i][j][1][1]=1LL<<60;
                //每个状态初值设成无穷大
                if(i!=1){
                    if(a[i][j]==a[i-1][j]){
                        f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][0][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][1][0]+r[i]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][0][1]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][1][1]+r[i]);
                    }else{
                        f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][1][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][0][0]+r[i]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][1][1]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][0][1]+r[i]);
                    }
                }
                if(j!=1){
                    if(a[i][j]==a[i][j-1]){
                        f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][0]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][0]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][1]+c[j]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][1]+c[j]);
                    }else{
                        f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][1]);
                        f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][1]);
                        f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][0]+c[j]);
                        f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][0]+c[j]);
                    }
                }
                //楠楠的转移
            }
        }
    }
    printf("%lld\n",min(min(f[h][w][0][0],f[h][w][0][1]),min(f[h][w][1][0],f[h][w][1][1])));
    //答案是走到右下角每种情况的最小值
    return 0;
}

·注意

一定要开long long,否则参见WA提交记录

转移需注意(别整错了)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值