USACO2.2.4 Party Lamps 派对灯 解题报告(枚举)

Description

在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)上的数值和经过若干操作后所有灯的状态。写一个程序去找出所有灯最后可能的与所给出信息相符的状态,并且没有重复。

Input

不会有灯会在输入中出现两次。 第一行: N。 第二行: C最后显示的数值。 第三行: 最后亮着的灯,用一个空格分开,以-1为结束。 第四行: 最后关着的灯,用一个空格分开,以-1为结束。

Output

每一行是所有灯可能的最后状态(没有重复)。每一行有N个字符,第1个字符表示1号灯,最后一个字符表示N号灯。0表示关闭,1表示亮着。这些行必须从小到大排列(看作是二进制数)。 如果没有可能的状态,则输出一行'IMPOSSIBLE'。

Sample Input

10
1
-1
7 -1

/*在这个样例中,有10盏灯,只有1个按钮被按下。最后7号灯是关着的。*/

Sample Output

0000000000
0101010101
0110110110

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

所有灯都关着 
1,4,7,10号灯关着,2,3,5,6,8,9亮着。 
1,3,5,7,9号灯关着,2, 4, 6, 8, 10亮着。*/

 

每个的灯最开始的时候是亮着的,现在按了C次开关,但是不知道每次按的是哪一个,让求按到最后每盏灯可能的状态

 

思路:

现在一共有4种按开关的方式,而每一种其实就只有开/关两种状态(因为同一个开关按3次和按一次效果是一样的),所以我们可以直接枚举每一种按法,然后判断是否符合条件就行了

这里我优化了一下,设了一个flag变量,然后按1开关(全部取反)就将flag取反,最后灯的状态可以用当前灯的状态^flag得到(相同为0不同为1,flag为0的话异或不变,flag为1的话异或取反)

 

#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(a) (a&(-a))
#define _mid(a,b) ((a+b)/2)
#define _mem(a,b) memset(a,0,(b+3)<<2)
#define fori(a) for(int i=0;i<a;i++)
#define forj(a) for(int j=0;j<a;j++)
#define ifor(a) for(int i=1;i<=a;i++)
#define jfor(a) for(int j=1;j<=a;j++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IN freopen("in.txt","r",stdin)
#define OUT freopen("out.txt","w",stdout)
#define IO do{\
    ios::sync_with_stdio(false);\
    cin.tie(0);\
    cout.tie(0);}while(0)
using namespace std;
typedef long long ll;
const int maxn =  1500+10;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
const double EPS = 1e-7;
const double Pi = acos(-1);
const int MOD = 1e9+7;
bool a[105];
int ON[105];
int OFF[105];
char bufs[105];
string res[1<<4|1];
bool flag;
int c,con,cof;
int n;
bool getbit(int x,int i) {      //判断i号开关是否按下
    return (x>>i)&1;
}
void change(int x) {
    if(getbit(x,0)==1)        //按了1号开关
        flag = true;
    ifor(n) {
        if(getbit(x,1)&&i%2)    //按了2号开关
            a[i] = !a[i];
        if(getbit(x,2)&&i%2==0)//按了3号开关
            a[i] = !a[i];
        if(getbit(x,3)&&(i-1)%3==0)//按了4号开关
            a[i] = !a[i];
    }
}
 
bool juge() {
    fori(con)
    if(!(a[ON[i]]^flag))
        return false;
    fori(cof)
    if(a[OFF[i]]^flag)
        return false;
    return true;
}
 
 
int main() {
    IO;
    //IN;
    con = cof = 0;
    int cnt = 0;
    cin >> n >> c;
    ifor(n)
        a[i] = 1;
    do {
        cin >> ON[con];
    } while(ON[con++]!=-1);
 
    do {
        cin >>OFF[cof];
    } while(OFF[cof++]!=-1);
    cof--,con--;
 
    fori(1<<4) {
//这里是从0000到1111遍历,能找出开关所有状态

        int fun = __builtin_popcount(i);    
//编译器内置函数,用于求i的二进制数中'1'的个数
        if(fun%2==c%2&&fun<=c) { //奇偶性相等且次数必须少于或等于按开关的次数
            flag = false;
            change(i);
            if(juge()) {
                int buf = 0;
                ifor(n)
                    bufs[buf++] = (a[i]^flag)+'0';
                bufs[buf] = 0;
                res[cnt++] = bufs;
            }
            change(i);
        }
    }
    if(cnt){
    sort(res,res+cnt);
    fori(cnt)
        cout << res[i] << endl;
    }
    else
        cout <<"IMPOSSIBLE" << endl;
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值