USACO:2.2.4 Party Lamps 派对灯

USACO:2.2.4  Party Lamps 派对灯


一、题目描述

★Party Lamps 派对灯

在IOI98 的节日宴会上,我们有N(10<=N<=100)盏彩色灯,他们分别从1 到N 被标上号码.

这些灯都连接到四个按钮:
按钮1:当按下此按钮,将改变所有的灯:本来亮着的灯就熄灭,本来是关着的灯被点亮.
按钮2:当按下此按钮,将改变所有奇数号的灯.
按钮3:当按下此按钮,将改变所有偶数号的灯.
按钮4:当按下此按钮,将改变所有序号是3*K+1(K>=0)的灯.例如:1,4,7...
一个计数器C 记录按钮被按下的次数.
当宴会开始,所有的灯都亮着,此时计数器C 为0.
你将得到计数器C(0<=C<=10000)上的数值和经过若干操作后某些灯的状态. 写一个程序去找出所有的灯最后可能的与所给出信息相符的状态,并且没有重复.
PROGRAM NAME: lamps
INPUT FORMAT

不会有灯会在输入中出现两次.
第一行: N.
第二行: C 最后显示的数值.
第三行: 最后亮着的灯,用一个空格分开,以-1 为结束.
第四行: 最后关着的灯,用一个空格分开,以-1 为结束.
SAMPLE INPUT (file lamps.in)
10
1
-1
7 -1
在这个样例中,有10 盏灯,只有1 个按钮被按下.最后7 号灯是关着的.
OUTPUT FORMAT
每一行是所有灯可能的最后状态(没有重复).每一行有N 个字符,第1 个字符表示1 号灯,最后一个
字符表示N 号灯.0 表示关闭,1 表示亮着.这些行必须从小到大排列(看作是二进制数).
如果没有可能的状态,则输出一行'IMPOSSIBLE'.
SAMPLE OUTPUT (file lamps.out)
0000000000
0101010101
0110110110

在这个样例中,有三种可能的状态:

所有灯都关着

1,4,7,10 号灯关着,2,3,5,6,8,9 亮着.

1,3,5,7,9 号灯关着,2, 4, 6, 8, 10 亮着.

二、题目解答

   每个开关按钮按2次和没按效果是一样的。所以每个按钮或者按或者不按,一共有2^4=16中开关状态。枚举每个按钮是否按下,然后生成结果,看是否满足条件,排序输出即可(注意判重)。

   另外,根据四种开关的操作效果,灯状态的周期为6,因此当N>=6时只需处理前6个灯, 排序时转换为10进制数, 输出时反复输出二进制的前6个的状态.


源代码

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <algorithm>

using namespace std;

int N,C;
vector<int> final_on,final_off;
string s1;
map<int,bool> mymap;//第一个为key,int类型,必须唯一。
int state6[16];//利用N个灯的周期性,只要考虑前6个灯即可;最多16种状态

int check(string s){
    for(int i=0;i<final_on.size();i++)
        {if(s[(final_on[i]-1)%6]=='0') return 0;}
    for( int i=0;i<final_off.size();i++)
        {if(s[(final_off[i]-1)%6]=='1')return 0;}
    return 1;
    }

int main()
{
    ifstream fin ("lamps.in");
    ofstream out("lamps.out");
    fin>>N;
    fin>>C;

    for(int i=0;fin>>i&&i!=-1;i++)
        final_on.push_back(i);
     for(int i=0;fin>>i&&i!=-1;i++)
        final_off.push_back(i);

//从开关的状态对应前6个灯的状态,最高位为第一个开关

//关于状态的记录,用一个int存储N个灯的前6位的状态就行了。
//状态的改变,通过开关,4种操作也很简单,用异或就可以搞定:
//操作1:异或(111111)=63 二进制数 等价于取反
//操作2:异或(010101)=21 改变偶数位
//操作3:异或(101010)=42 改变奇数位
//操作4:异或(100100)=36 改变1、4、7位
    int j=0;
    int c=C%2;
    int newstate;

    if(c==0){
        newstate=~0;
        if(mymap[newstate]==0)  {state6[j++]=newstate;mymap[newstate]=1;}//操作1

        if(C>1){
            //****0011 0101 0110 1001 1010 1100 状态3,5,6,9,10,12;
			//只有两个开关为打开状态,C可能为2+2的幂次
            //0011
            newstate=~42^36;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           //0101
            newstate=~36^21;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           //0110
            newstate=~42^21;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           //1001
            newstate=~36^63;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           //1010
            newstate=~42^63;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           //1100
            newstate=~21^63;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//

           //****1111 状态15;C可能为4+2的幂次
           if(C>=4){
             newstate=~21^63^36^42;
            if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
           }
        }

    }

    else {//C为奇数

        //****0001 0010 0100 1000 状态1,2,4,8;
		//只有一个开关为打开状态,C可能为1+2的幂次,奇数
        //0001
        newstate=~36;
        if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
        //0010
        newstate=~42;
        if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
        //0100
        newstate=~21;
        if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
        //1000
        newstate=~63;
        if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//

        //****0111 1101 1011 1110 状态7,13,11,14;可能为3+2的幂次
        if(C>2){
                //0111
                newstate=~21^42^36;
                if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
                //1101
                newstate=~63^21^36;
                if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
                //1011
                newstate=~63^42^36;
                if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
                //1110
                newstate=~63^42^21;
                if(mymap[newstate]==0) { state6[j++]=newstate; mymap[newstate]=1;}//
        }
    }

//16种开关状态 (对应4种开关的状态)

//0000 状态0;C可能为0+2的幂次(等效按开关0次)
//1111 状态15;C可能为4+2的幂次(等效按开关4次)
//0011 0101 0110 1001 1010 1100 状态3,5,6,9,10,12;C可能为2+2的幂次(等效按开关2次)
//0001 0010 0100 1000 状态1,2,4,8;C可能为1+2的幂次,奇数(等效按开关1次)
//0111 1101 1011 1110 状态7,13,11,14;可能为3+2的幂次(等效按开关3次)
    sort(state6,state6+j);//
    int flag=0;
    for( int i=0;i<j;i++){
        int  decimal = state6[i];;			
		string s,ss;
		for(int j=5;j>-1;j--){
			if((decimal>>j)&1) ss="1";//整数化为二进制的字符串
			else	ss="0";
			s=s+ss;
		}

        if(check(s)){
            for(int i=0;i<N/6;i++) out<<s;
            string sTemp=s.substr(0,N%6);
            out<<sTemp<<endl;
            flag=1;
        }
    }
    if(!flag) {out<<"IMPOSSIBLE"<<endl;}
    return 0;

}

官方答案

//There are a number of insights required for this problem.
//The main insight is that no matter which switches get pressed, the light pattern will repeat every six lamps.
//Another insight is that the order of switch presses does not matter,
//and that pressing a switch twice is the same as not pressing a switch at all.
//Any even number of switch presses greater than four might as well just be four switch presses,
//and any odd number greater than three might as well just be three presses.
//We can treat the light information as describing only the first six lamps (by treating lamp n as lamp (n mod 6)),
//and only try pressing each switch once or not at all.
//To maintain the actual information about which lights are on, we use an integer holding six bits.
//The least significant bit is light 6, the next least is light 5, and so on.
//This has the effect that treating these bit strings as normal numbers sorts the same way that we need to print them.
//Switches are represented as bit vectors that say which lights get toggled.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXLAMP	6  //定义最大灯数 6,因为灯的状态具有循环特性。
#define LAMPMASK	((1<<MAXLAMP)-1)//低六位为111 111

int nlamp;
int nswitch;
int ison;
int known;

int poss[1<<MAXLAMP];//1左移6位

int flip[4] = {//翻转
    LAMPMASK,		    // flip all lights */低6位 111 111
    LAMPMASK & 0xAA, 	//flip odd lights */ 低6位 101 010
    LAMPMASK & 0x55,	//flip even lights */低6位 010 101
    LAMPMASK & ((1<<(MAXLAMP-1))|(1<<(MAXLAMP-4)))	//lights 1, 4 低6位100 100
};

 Starting with current light state `lights', flip exactly n switches
 with number >= i.
void
search(int lights, int i, int n)
{
    if(n == 0) {
        if((lights & known) == ison)
            poss[lights] = 1; //
        return;
    }

    for(; i<4; i++)     //开关操作递增,不会重复。每个类型操作,最多操作一次(因为重复没有意义,是指操作总数化简后)
        search(lights ^ flip[i], i+1, n-1); //异或,相当于一次开关操作
}

void
printseq(FILE *fout, int lights)//输出结果
{
    int i;
    char s[100+1];

    for(i=0; i<nlamp; i++)
        s[i] = (lights & (1<<(MAXLAMP-1 - i%MAXLAMP))) ? '1' : '0';
    s[nlamp] = '\0';
    fprintf(fout, "%s\n", s);
}

int
main(void)
{
    FILE *fin, *fout;
    int a, i, impossible;

    fin = fopen("lamps.in", "r");
    fout = fopen("lamps.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d %d", &nlamp, &nswitch);

    for(;;) {
        fscanf(fin, "%d", &a);
        if(a == -1)
            break;
        a = MAXLAMP-1 - (a-1) % MAXLAMP;
        ison |= 1<<a; //或,将a中最后状态为on的灯存储为1
        known |= 1<<a;//或,know中存储状态已知的灯的位置
    }

    for(;;) {
        fscanf(fin, "%d", &a);
        if(a == -1)
            break;
        a = MAXLAMP-1 - (a-1) % MAXLAMP;
        assert((ison & (1<<a)) == 0);//或,是否出现已知状态间自相矛盾
        known |= 1<<a;//或,know中存储状态已知的灯的位置
    }
    因为4个开关,每个只有两种状态0/1,化简化后最多4次开关操作。//可以分为奇数和偶数两种情况
    if(nswitch > 4){
        if(nswitch%2 == 0)
            nswitch = 4;//偶数情况,4/2/0
        else
            nswitch = 3;//奇数情况,3/1
    }

    for(; nswitch >= 0; nswitch -= 2)//搜索对应情况
	    search(LAMPMASK, 0, nswitch);//LAMPMASK灯的原始状态,0代表第一种开关操作,nswitch代表最多几次开关操作

    impossible = 1;
    for(i=0; i<(1<<MAXLAMP); i++) {
        if(poss[i]) { //从小到大搜索,所以输出的时候肯定是按从小到大的顺序
            printseq(fout, i);
            impossible = 0;
        }
    }
    if(impossible)
        fprintf(fout, "IMPOSSIBLE\n");

    exit(0);
}




由于自身是初学者,编程能力有限,未达到专业程序员的水平,可能误导大家,请大家甄读;文字编辑也一般,文中可能会有措辞不当。博文中的错误和不足敬请读者批评指正。

部分引自  http://pingce.ayyz.cn:9000/usaco/20110129214306/lamps_001.html




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝亦

感谢博主辛勤的付出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值