题解:ABC283E - Don‘t Isolate Elements

题解:ABC283E - Don't Isolate Elements

·题目

链接:Atcoder。

链接:洛谷。

·难度

算法难度:B。

思维难度:A。

调码难度:A。

综合评价:普及+/提高。

·算法

动态规划。

·思路

状态:f[i][0/1][0/1][0/1],表示考虑前i行,其中第i-1行是否选取、第i行是否选取、第i+1行是否选取,保证前i-1行一定没有孤点,若能使得第i行没有孤点,则为最小做几次操作,否则为-1。

转移:先遍历第i行每一项并,记录该点与周围的点变换后是否满足周围的每一个点都不等于该点,若满足则表示这个状态肯定是-1,否则表示可以尝试转移。若可以转移,则从f[i-1][0/1][j][k]这两个状态中选取最优值转移到f[i][j][k][l](当前状态)。

初值:用和转移类似的方法跑完所有f[1][0/1][0/1][0/1]。

·代价

O(nm),或者说是O(hw),但是常数大。

·细节

对于不存在的周围的点,我们采用两种方式解决:

①特殊判断,发现这种不存在的周围的点不予考虑。

②假性编号,通过一种编号方式使得它与原点一定不相同,但是在变换时容易寄掉。

·代码

#include
#define M 1100
#define N 1100
using namespace std;
int f[N][2][2][2]={},a[N][M]={},ans=-1,m=0,n=0;
//ans就是答案
int change(int d,int x);
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    //输入部分
    memset(f,-1,sizeof(f));
    //f初始值为-1,表示“无法完成”
    for(int i=1;i<=m;i++){
        a[n+1][i]=1-a[n][i];
    }
    //对于第n行往下的假性编号,但是在后期变换时仍然需要特殊判断
    for(int i=0;i<=1;i++){
        for(int j=0;j<=1;j++){
            a[1][0]=1-a[1][1];
            a[1][m+1]=1-a[1][m];
            //最左侧的左侧和最右侧的右侧的假性编号:最左侧和最右侧的编号取反
            bool flag=true;
            for(int k=1;k<=m;k++){
                //由于1不存在任何前面的点,不需要用一层循环遍历“i-1行的变换情况”
                int d=a[1][k],d1=a[1][k-1],d2=a[1][k+1],d3=a[2][k];
                d=change(d,i);
                d1=change(d1,i);
                d2=change(d2,i);
                d3=change(d3,j);
                //取出周围的点变换后的编号
                if(d!=d1&&d!=d2&&d!=d3){
                    flag=false;
                    //d:我被孤立啦!
                }
            }
            //上面就是遍历第i行每一项判断是否有孤点
            if(flag==true){
                f[1][0][i][j]=i;
                f[1][1][i][j]=i;
                //如果的没有被孤立就可以赋值————根据状态判断第一行是否被操作
            }
        }
    }
    //上面是dp初始值
    for(int i=2;i<=n;i++){
        for(int j=0;j<=1;j++){
            for(int k=0;k<=1;k++){
                for(int l=0;l<=1;l++){
                    //遍历每个状态
                    a[i][0]=1-a[i][1];
                    a[i][m+1]=1-a[i][m];
                    //最左侧的左侧和最右侧的右侧的假性编号:最左侧和最右侧的编号取反
                    bool flag=true;
                    for(int mm=1;mm<=m;mm++){
                        int d=a[i][mm],d1=a[i][mm-1],d2=a[i][mm+1],d3=a[i-1][mm],d4=a[i+1][mm];
                        d=change(d,k);
                        d1=change(d1,k);
                        d2=change(d2,k);
                        d3=change(d3,j);
                        //求出周围点变换后的编号
                        if(i!=n){
                            d4=change(d4,l);
                        }else{
                            d4=change(d4,k);
                        }
                        //i为n时的假性编号其实比较失败,还是需要特殊判断(要和d的变化保持同步)
                        if(d!=d1&&d!=d2&&d!=d3&&d!=d4){
                            flag=false;
                            //d被孤立
                        }
                    }
                    if(flag==true){
                        //没有孤点,尝试转移
                        int x=f[i-1][0][j][k],y=f[i-1][1][j][k];
                        //两个前辈
                        int ret=0;
                        if(x!=-1&&y!=-1){
                            ret=min(x,y);
                        }else{
                            if(x==-1&&y!=-1){
                                ret=y;
                            }else{
                                if(y==-1&&x!=-1){
                                    ret=x;
                                }else{
                                    ret=-1;
                                }
                            }
                        }
                        //对于复杂的最优值处理
                        if(ret!=-1){
                            f[i][j][k][l]=ret+k;
                            //在能够达成目标的情况下不要忘记第i行也被反转了,因此转移时要加上k
                        }
                    }
                    if(i==n){
                        if(ans==-1){
                            if(f[i][j][k][l]!=-1){
                                ans=f[i][j][k][l];
                            }
                        }else{
                            if(f[i][j][k][l]!=-1){
                                if(f[i][j][k][l]

·注意

一定要把“细节”里面的东西想明白。

你可能感兴趣的:(c++,dp,算法)