[poj]开关类问题 枚举 位运算

poj 1222  EXTENDED LIGHTS OUT

开关只有两种方案 按和不按,按两次相当于关

只用枚举第一排开关的按法即可,剩下的行为使上一排的灯全部关闭,按法可以确定,并且是唯一的。

最后检查最后一行是否都为0,都为0为可行的方案。  用位运算来实现。

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

char ol[5], l[5], r[5];
int t;

int getBit(char c, int i)
{
    return 1 & (c>>i);
}

void setBit(char &c, int i, int v)
{
    if (v) {
        c |= (1 << i);
    }
    else 
        c &= ~(1 << i);
}

void FlipBit(char &c, int i)
{
    c ^= (1 << i);
}

void output()
{
    cout << "PUZZLE #" << t << endl;
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 6; j++) {
            cout << getBit(r[i], j);
            if (j < 5)
                cout << " ";
        }
        cout << endl;
    }
}

int main()
{
    //freopen("1.txt", "r", stdin);
    int T;
    cin >> T;
    for (t = 1; t <= T; t++) {
        for (int i = 0; i < 5; i++)
            for (int j = 0; j < 6; j++) {
                int s;
                cin >> s;
                setBit(ol[i], j, s);
            }

        for (int n = 0; n < 64; n++) {  //枚举第一列方案0~2^6-1
            memcpy(l, ol, sizeof(ol));
            int switchs = n; //当前行的开关状态
            for (int i = 0; i < 5; i++) {
                r[i] = switchs;
                for (int j = 0; j < 6; j++) {
                    if (getBit(switchs, j)) {
                        if (j > 0)
                            FlipBit(l[i], j-1);
                        FlipBit(l[i], j);
                        if (j < 5)
                            FlipBit(l[i], j+1);
                    }
                }
                if (i < 4)
                    l[i+1] ^=switchs; 
                switchs = l[i];  //i+1行灯的状态由第i行灯的状态决定
            }
            if (l[4] == 0) {
                output();
                break;
            }
        }
    }


    return 0;
}

特殊密码锁

描述

有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。

然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。

当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。

输入两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。输出至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。

样例输入

011
000

样例输出

1

类似的思想,枚举第一个的状态,两种可能,那剩下的就可以唯一确定了,最后检查最后一位即可
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <string>
using namespace std;
#define INF 0x3f3f3f3f
int src, des, r, t;
int ans;

int getBit(int c, int i)
{
    return 1 & (c >> i);
}

void setBit(int &c, int i, int v)
{
    if (v)
        c |= (1 << i);
    else
        c &= ~(1 << i);
}

void flipBit(int &c, int i)
{
    c ^= (1 << i);
}

int main()
{
    //freopen("1.txt", "r", stdin);
    string a;
    cin >> a;
    int len = a.length();
    for (int i = 0; i < len; i++) 
        setBit(src, i, a[i]-'0');
    
    cin >> a;
    for (int i = 0; i < len; i++)
        setBit(des, i, a[i]-'0');

    int Min, cnt;
    for (int p = 0; p < 2; p++) {
        Min = INF; cnt = 0;
        t = src;
        if (p) {
            flipBit(t, 0);
            flipBit(t, 1);
            cnt++;
        }
        for (int i = 1; i < len; i++) {
            if (getBit(t, i-1) != getBit(des, i-1)) {
                cnt++;
                flipBit(t, i);
                if (i < len-1)
                    flipBit(t, i+1);
            }
        }
        if (getBit(t, len-1) == getBit(des, len-1)) {
            if (cnt < Min) {
                Min = cnt;
                ans = cnt;
            }
        }
    }
    if (Min == INF)
        cout << "impossible";
    else
        cout << ans;

    return 0;
}

poj 1166 拨钟问题

有9个时钟,排成一个3*3的矩阵。

|-------|    |-------|    |-------|
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C

|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F

|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I
(图 1)

现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。


移动    影响的时钟

1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI

 

输入9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。输出输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。样例输入

3 3 0 
2 2 2 
2 1 2 

样例输出

4 5 8 9 

每个钟表有4种拨法,且与顺序无关,枚举即可 4^9种可能
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f;
int ori[4][4], s[4][4];
int t[10];

bool ok()
{
    for (int i = 1; i <=3; i++) {
        for (int j = 1; j <=3; j++) {
            if (s[i][j])
                return 0;
        }
    }
    return 1;
}
int main()
{
    //freopen("1.txt", "r", stdin);
    for (int i = 1; i <= 3; i++)
        for (int j = 1; j <= 3; j++)
            cin >> ori[i][j];

    int Min = INF;
    for (int i1 = 0; i1 < 4; i1++) 
    for (int i2 = 0; i2 < 4; i2++) 
    for (int i3 = 0; i3 < 4; i3++) 
    for (int i4 = 0; i4 < 4; i4++) 
    for (int i5 = 0; i5 < 4; i5++) 
    for (int i6 = 0; i6 < 4; i6++) 
    for (int i7 = 0; i7 < 4; i7++) 
    for (int i8 = 0; i8 < 4; i8++) 
    for (int i9 = 0; i9 < 4; i9++) {
        memcpy(s, ori, sizeof(ori));   //每种枚举情况都是独立的
        s[1][1] = (s[1][1]+i1+i2+i4)%4;  //A
        s[1][2] = (s[1][2]+i1+i2+i3+i5)%4;//B
        s[1][3] = (s[1][3]+i2+i3+i6)%4;//C
        s[2][1] = (s[2][1]+i1+i4+i5+i7)%4;//D
        s[2][2] = (s[2][2]+i1+i3+i5+i7+i9)%4;//E
        s[2][3] = (s[2][3]+i3+i5+i6+i9)%4;//F
        s[3][1] = (s[3][1]+i4+i7+i8)%4;//G
        s[3][2] = (s[3][2]+i5+i7+i8+i9)%4;//H
        s[3][3] = (s[3][3]+i6+i8+i9)%4;//I
        if (ok()) {
            int tot = i1+i2+i3+i4+i5+i6+i7+i8+i9;
        //    cout << tot << endl;
            if (tot < Min) {
                Min = tot;
                t[1] = i1; t[2] = i2; t[3] = i3;
                t[4] = i4; t[5] = i5; t[6] = i6;
                t[7] = i7; t[8] = i8; t[9] = i9;
            }
        }

    }
    for (int i = 1; i <= 9; i++) {
        if (t[i] && i!=9) {
            for (int j = 0; j < t[i]; j++)
                cout << i << " ";
        }
        else if (t[i] && i == 9) {
            for (int j = 0; j < t[i]-1; j++)
                cout << i << " ";
            cout << i;
        }
    }


    return 0;
}

 


posted @ 2017-07-20 15:12  whileskies  阅读(271)  评论(0编辑  收藏  举报