UVa 10318 Security Panel 解题报告(暴力,中途相遇法)

56 篇文章 0 订阅

10318 - Security Panel

Time limit: 3.000 seconds

Security Panel

Input: standard input

Output: standard output

Time Limit: 2 seconds

Memory Limit: 32 MB

 

Advanced Control Mechanisms (ACM) produces sophisticated electronic locks and security devices.

The company's most recent invention is a panel of illuminated buttons in r rows and c columns. The buttons are numbered left-to-right, top-to-bottom, starting at 1 in the upper-left corner. Each button has two states: lit and unlit. Initially, all buttons are unlit. Pressing a button switches the state of some buttons from lit to unlit (or vice-versa) according to a 3x3 pattern. Pressing a button on a panel applies the pattern centered on that button. To unlock the panel, the buttons must be pressed in such a way so as to light all of them.

For example, consider the following pattern where pressing a button switches the state of the button pressed, as well as the button above and the buttons to the upper and lower left.


 

If we use this pattern on a 2x3 panel, then pressing buttons 25, and 6 will light all the buttons. If pressed in that order, the state changes of the panel are: (Light green means lights are off and vice versa)

 

 

Input

Each input case will begin with the number of rows and columns on the panel, 1 <= r, c <= 5 alone on a line. The next three lines describe how pressing a button will affect the nearby lights. This description consists of a 3x3 character grid, where the character "*" indicates that the light in that position switches state (from lit to unlit or from unlit to lit) while "." means its state remains unchanged.

Input ends with 0 0 alone on a line.


Output

For each input case, output "Case #" followed by the number of the case. If there is no way to turn on all the lights, print"Impossible." If it is possible to turn on the lights, output the buttons to be pressed in increasing order, separated by single space. Output the answer that requires the fewest number of buttons possible to be pressed. If there is more than one correct solution, anyone will do.

 

Sample Input

2 3
**.
.*.
*..
4 5
.*.
***
.*.
2 2
...
.**
...
4 3
*.*
...
..*
0 0

 

Sample Output

Case #1
2 5 6
Case #2
2 3 4 7 9 12 14 17 18 19 
Case #3
1 3 
Case #4
Impossible.

    解题报告: 这道题方法有很多。直接暴力,加剪枝的暴力,高斯消元都可以。这里用的是中途相遇法。枚举前一半开关在初始状态下可能达到的所有状态,枚举后一半开关在终止状态下能达到的所有状态。如果有遇上的,那么更新答案。复杂度为O(2^(n/2))。

    其他方法就懒得写了。注意题目要求输出按开关数最少的方案,所以要枚举所有的情况,找出最少的答案。代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <iomanip>
using namespace std;
#define ff(i, n) for(int i=0;i<(n);i++)
#define fff(i, n, m) for(int i=(n);i<=(m);i++)
#define dff(i, n, m) for(int i=(n);i>=(m);i--)
typedef long long LL;
typedef unsigned long long ULL;
void work();
int main()
{
#ifdef ACM
    freopen("in.txt", "r", stdin);
#endif // ACM
    work();
}

/***************************************************/

int n, m, mid;
int flipPos;
int ans;
bool change[5][5];
bool maze[10][10];

map<int, int> mm;

void flip(int x, int y)
{
    fff(i, -1, 1) fff(j, -1, 1)
        maze[x+i][y+j] ^= change[i+2][j+2];
}

int getSt()
{
    int st = 0;
    fff(i, 1, n) fff(j, 1, m) if(maze[i][j])
        st ^= 1<<((i-1)*m+j-1);
    return st;
}

void dfs1(int pos, int end)
{
    if(pos > end)
    {
        int st = getSt();
        if(mm.count(st))
        {
            if(__builtin_popcount(mm[st]) > __builtin_popcount(flipPos))
                mm[st] = flipPos;
        }
        else
        {
            mm[st] = flipPos;
        }

        return;
    }

    // no flip
    dfs1(pos+1, end);

    // flip current block
    int x = (pos-1)/m+1;
    int y = (pos-1)%m+1;
    flip(x, y);
    flipPos ^= (1<<(pos-1));
    dfs1(pos+1, end);
}

void dfs2(int pos, int end)
{
    if(pos > end)
    {
        int st = getSt();
        if(mm.count(st))
        {
            int newAns = flipPos ^ mm[st];
            if(ans < 0 || __builtin_popcount(ans) > __builtin_popcount(newAns))
                ans = newAns;
        }
        return;
    }

    // no flip
    dfs2(pos+1, end);

    // flip current block
    int x = (pos-1)/m+1;
    int y = (pos-1)%m+1;
    flip(x, y);
    flipPos ^= (1<<(pos-1));
    dfs2(pos+1, end);
}

void work()
{
    int cas = 1;
    while(scanf("%d%d", &n, &m) == 2 && (n||m))
    {
        // read input data
        memset(change, 0, sizeof(change));
        fff(i, 1, 3)
        {
            char str[10];
            scanf("%s", str+1);
            fff(j, 1, 3) if(str[j] == '*')
                change[i][j] = true;
        }

        mm.clear();
        mid = n*m/2;

        flipPos = 0;
        memset(maze, 0, sizeof(maze));
        dfs1(1, mid);

        ans = -1;
        flipPos = 0;
        memset(maze, 1, sizeof(maze));
        dfs2(mid+1, m*n);

        printf("Case #%d\n", cas++);
        if(ans == -1)
        {
            puts("Impossible.");
        }
        else
        {
            vector<int> vint;
            ff(i, 25) if(ans & (1<<i))
                vint.push_back(i+1);
            if(vint.size()) printf("%d", vint[0]);
            fff(i, 1, vint.size()-1) printf(" %d", vint[i]);
            puts("");
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值