反转 开关问题

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

用数组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

时间: 2024-12-22 14:13:32

反转 开关问题的相关文章

挑战程序竞赛 反转开关 poj3276

这个我其实也没有看太懂它的证明过程. 1.若某一个位置被翻转了n次,则其实际上被翻转了n%2次. 2.分析易知翻转的顺序并不影响最终结果. 3.现在我们着眼于第1个位置,可知若要将第1个位置进行翻转只有翻转它自己,因为没有其他位置的翻转会引起它的翻转. 由①可知若第1个位置为1则必须且进行翻转(并将其后2个进行连带翻转)且以后不再进行翻转,因为再进行翻转就一共翻转了2次相当于没翻转. 然后着眼于第2个位置,由于第1个位置不再进行翻转,所以要想翻转第2个位置只有翻转它自己,因为没有其他位置的翻转会

D - Fliptile POJ3279 搜索(反转开关经典问题,二进制表示集合)

D - Fliptile Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3279 Description Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy acti

poj1484---判断保险丝是否烧断

题目输入要求: 2 2 10 //设备数n  接下来的操作数m   保险丝能承受最大电流c5 //电器1的电流7 //2的电流1 //反转开关12 //反转开关2 思路:设置一个flag数组,记得每次进入一个数据集,重新设为0,flag从1开始,flag[1]...flag[n]模拟开关状态 将每个device的电流分别存到ci数组里,从1开始存ci[1],存到ci[n],不管ci[0],n<=20,随便给个 大于21的数 接着,再用一个for循环,读入device序号,如果flag[dev]=

机电传动控制大作业 第一阶段

机电传动控制大作业 第一阶段 一.系统硬件接口定义 1.电梯内操作界面: 标有1-7数字的按钮(每个按钮有一个LED灯,按下按钮灯即亮),开门,关门以及紧急报警的按钮和楼层指示的LED数码管.电梯门的打开和关闭需要两个行程开关,接收到开关门的信息可以直接控制门的开关. 2.楼栋操作界面: 2-6层有上行和下行按钮,1层只有上行,7层只有下行.每个按钮都连接一个LED灯(按下按钮灯即亮).两个LED数码管显示所在的楼层. 3.动力相关硬件: 每部电梯配有一台交流异步变频电机和变频器,电机接口接电机

《机电传动控制》----学习笔记八

机械大楼电梯控制项目(第一阶段) 1.系统硬件接口定义 每层电梯门外的上行/下行按钮:留着接口接按钮开关 每层电梯门外的左/右LED数码管:留接口显示当前电梯所在的楼层 数码管旁的箭头LED灯:留接口接收电机正反转或停止等状态 每层配有的平层行程开关:留接口向PLC传递楼层信息,决定是否在该层停留 电梯内部的七个楼层的数字按键:需要接口向系统输入目的楼层信息 上述七个按键对应的指示灯:留接口显示系统获得的按键信息 电梯内部的开/关门按键:留接口触发电梯开启或关闭的相关控制电路 电梯内楼层指示数码

POJ3185 The Water Bowls 反转(开关)

Description The cows have a line of 20 water bowls from which they drink. The bowls can be either right-side-up (properly oriented to serve refreshing cool water) or upside-down (a position which holds no water). They want all 20 water bowls to be ri

反转(开关问题)

1. 对于一个特定的 K 如何求出让所有牛面朝前方的最小操作数.如果把牛的方向作为状态进行搜索的话,由于状态数有 2N 个,是无法在时限内得出答案的. 首先,交换区间反转的顺序对结果是没有影响的.此外,对同一区间进行两次以上的反转是多余的.由此,问题就转化成了求需要被反转区间的集合.先考虑一下最左端的牛.包含这头牛的区间就只有一个,因此如果这头牛面朝前方,我们就能知道这个区间不需要反转. 反之,如果这头牛朝后方,对应的区间就必须反转.而且在此之后这个最左的区间就不需要考虑了.不断重复下去,就可以

反转(开关问题) POJ 3276

POJ 3276 题意:n头牛站成线,有朝前有朝后的的,然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作m次使得它们全部面朝前方.问:求最小操作m,再此基础上求k. 题解:1.5000头牛不是小数目,再怎么也得要n^2的算法,其中,枚举k是需要的,这就有n了,只能想办法给出一个n在O(n)时间内求出最小次数了. 2.对于给定的k,要想O(n)内把次数算出来,即只能扫一遍,一想到的必定是从前往后扫,遇到面朝后的就转头,但这一转牵扯太多,要改太多东西,k一大直接崩溃. 3.对于每次扫描

彩扩机项目--开关滤波进阶,电机驱动桥,死区,三极管搭建反向电路

可以吧电动机,看做一个电感.有以下特性 1,电感的电流是渐变的 2,自感会阻碍电流的变化 3,自感方向和电流变化方向相反 4,电感是存储能量的,并且本身不耗能(以电流的方式存储) 电机加正向电压,正转,反向电压,反转. 为了使对角线的开关同时打开,使用了三极管,根据三极管作为开关管的场合,在上面使用的PNP三极管,在下面使用了NPN三极管. 但是NPN三极管B极需要高电平导通,PNP三极管B极需要低电平导通.为了使用一个电平控制一条对角线的一起开和关.使用两个三极管搭建类似非门的电路. 这样的电