反转 开关问题

首先考虑最左端的牛。包含这头牛的区间只有一个,因此如果这头牛面朝前方,这个区间不反转,面朝后方则反转。以此类推,逐渐缩小问题规模。

用数组j[i]=1代表区间[i,i+K-1]进行了反转  j[i]=0代表不反转。

如果一头牛之前被反转的次数为奇数,则朝向和刚开始相反,为偶数则相同。

#include<iostream>
#include<memory.h>
using namespace std;
int N,dir[5005];
int j[5005];   //标记区间[i,i-K+1]是否反转 反转则为1, 不反转为0
int calc(int K)
{
    memset(j,0,sizeof(j));
    int res=0; //反转次数
    int sum=0; //j的和 用来表示当前块之前所经历的反转次数
    for(int i=0;i+K<=N;i++)
    {
        //计算区间[i,i-K+1]
        if((dir[i]+sum)%2!=0)//需要进行反转
        {
            j[i]=1;
            res++;
        }
        sum+=j[i];
        if(i-K+1>=0)
        {
            //保持区间长度
            sum-=j[i-K+1];
        }
    }

    //检查剩下的K-1头牛是否有朝后的情况
    for(int i=N-K+1;i<N;i++)
    {
        //需要进行反转 无解
        if((dir[i]+sum)%2!=0)
        {
            return -1;
        }
        if(i-K+1>=0)
        {
            //保持区间长度
            sum-=j[i-K+1];
        }
    }
    return res;
}
int main()
{
    cin>>N;
    char c;
    for(int i=0;i<N;i++)
    {
        //牛的方向 0:F 1:B
        cin>>c;
        if(c=='F') dir[i]=0;
        else dir[i]=1;
    }
    //最少操作次数 与之对应的最小操作长度
    int M=N,K=1;
    for(int i=1;i<=N;i++)
    {
        int x=calc(i);
        if(x>0&&x<M)
        {
            K=i;
            M=x;
        }
    }
    cout<<K<<" "<<M<<endl;
    return 0;
}
poj3276

 

先指定好第一行的翻转方法。此时能够翻转(1,1)的就只剩下(2,1)了,可以直接判断(2,1)是否需要翻转。

类似的(2,1)~(2,N)都能这样判断,如此反复下去就可以确定所有各自的翻转方法。

最后(M,1)~(M,N)如果并非全为白色,就意味着不存在可行的操作方法。

我们需要枚举第一行的所有翻转方法。

#include<iostream>
#include<memory.h>
using namespace std;
const int dx[5]= {-1,0,0,0,1};
const int dy[5]= {0,-1,0,1,0};
int M,N;
int tile[16][16]; //起始
int opt[16][16];  //保存最优解
int flip[16][16]; //保存中间结果

//查询(x,y)的颜色
int get(int x,int y)
{
    int c=tile[x][y];
    for(int i=0; i<5; i++)
    {
        int x2=x+dx[i];
        int y2=y+dy[i];
        if(x2>=0&&y2>=0&&x2<M&&y2<N)
        {
            c+=flip[x2][y2];
        }
    }
    return c%2;
}

//求出第一行确定情况下的最小操作次数
//不存在解的话返回-1
int calc()
{
    //求出从第二行开始的翻转方法
    for(int i=1; i<M; i++)
    {
        for(int j=0; j<N; j++)
        {
            if(get(i-1,j)!=0)
            {
                //(i-1,j)是黑色的话 必须翻转这个格子
                flip[i][j]=1;
            }
        }
    }

    //判断最后一行是否全白
    for(int j=0; j<N; j++)
    {
        if(get(M-1,j)!=0)
        {
            return -1; //无解
        }
    }

    //统计翻转的次数
    int res=0;
    for(int i=0; i<M; i++)
    {
        for(int j=0; j<N; j++)
        {
            res+=flip[i][j];
        }
    }
    return res;
}

int main()
{
    cin>>M>>N;
    for(int i=0; i<M; i++)
    {
        for(int j=0; j<N; j++)
        {
            cin>>tile[i][j];
        }
    }

    int res=-1;
    //按照字典序尝试第一行的所有可能性
    for(int i=0; i<1<<N; i++)
    {
        memset(flip,0,sizeof(flip));
        for(int j=0; j<N; j++)
        {
            flip[0][N-j-1]=i>>j&1;
        }
        int num=calc();
        if(num>=0&&(res<0||res>num))
        {
            res=num;
            memcpy(opt,flip,sizeof(flip));
        }
    }

    if(res<0)
    {
        //无解
        cout<<"IMPOSSIBLE"<<endl;
    }
    else
    {
        for(int i=0; i<M; i++)
        {
            for(int j=0; j<N; j++)
            {
                cout<<opt[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}
poj3279

 

posted @ 2017-03-03 00:03  docyard  阅读(558)  评论(0编辑  收藏  举报