常用技巧:反转/开关问题

这类型题目的特点是,要反转一个点的状态,会顺带反转周围一个范围内的状态,或者说是只能对一个范围进行反转。

针对这类题目,要把握的有两点:

1)每个区间最多也就是被反转一次,两次及其以上是没有意义的

2)分析每个点受几个区间影响或者是对几个区间产生影响,对当前点考虑反转时,优先满足已经走过的点(上一层或上一个)的可行性,而不必考虑自己,自己的问题留给下一个和自己有关的点考虑。当这个过程结束后,看看最后一批点满不满足条件,就可以知道当前情况可不可行以及当前可能的最小解。

例题1,POJ3276,是在一行上操作,要找出最小操作数以及最小操作数时的操作范围

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n,d[5005],f[5005],ans1,ans2;
int cal(int k){
    memset(f,0,sizeof(f));
    int sum=0,res=0;
    for(int i=0;i+k-1<n;i++){
        if((d[i]+sum)%2!=0){
            f[i]=1;
            res++;
        }
        sum+=f[i];
        if(i-k+1>=0){
            sum-=f[i-k+1];
        }
    }
    for(int i=n-k+1;i<n;i++){
        if((d[i]+sum)%2!=0) return -1;
        if(i-k+1>=0){
            sum-=f[i-k+1];
        }
    }
    return res;
}
int main(){
    scanf("%d",&n);
    char c;
    for(int i=0;i<n;i++){
        cin>>c;
        if(c=='B') d[i]=1;
        else if(c=='F') d[i]=0;
    }
    ans2=0x7fffffff;
    for(int i=1;i<=n;i++){
        int m=cal(i);
        if(m>=0&&m<ans2){
            ans1=i;
            ans2=m;
        }
    }
    printf("%d %d\n",ans1,ans2);
    return 0;
}

例题2,POJ3279,在二维数组里操作

#include<stdio.h>
#include<string.h>
int m,n,maze[20][20],flip[20][20],res[20][20],go[5][2]={0,0,1,0,0,-1,-1,0,0,1};
int getcol(int x,int y){
    int cnt=maze[x][y];
    for(int i=0;i<5;i++){
        int nx=x+go[i][0],ny=y+go[i][1];
        if(nx<0||ny<0||nx>=m||ny>=n) continue;
        cnt+=flip[nx][ny];
    }
    return cnt%2;
}
int cal(){
    for(int i=1;i<m;i++){
        for(int j=0;j<n;j++){
            if(getcol(i-1,j)==1) flip[i][j]=1;
        }
    }
    for(int i=0;i<n;i++){
        if(getcol(m-1,i)==1) return -1;
    }
    int cnt=0;
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            cnt+=flip[i][j];
        }
    }
    return cnt;
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            scanf("%d",&maze[i][j]);
        }
    }
    int maxstate=(1<<n)-1,ans=0x7fffffff;
    for(int i=0;i<=maxstate;i++){
        memset(flip,0,sizeof(flip));
        for(int j=0;j<n;j++) flip[0][n-1-j]=(i>>j)&1;
        int tmp=cal();
        if(tmp>=0&&tmp<ans){
            ans=tmp;
            for(int i=0;i<m;i++){
                for(int j=0;j<n;j++){
                    res[i][j]=flip[i][j];
                }
            }
        }
    }
    if(ans==0x7fffffff) printf("IMPOSSIBLE\n");
    else{
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(j!=0) printf(" ");
                printf("%d",res[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

推荐练习题:POJ3185POJ1222

posted @ 2020-10-09 15:16  太山多桢  阅读(174)  评论(0)    收藏  举报