每日一题7.19

P10449 费解的开关 - 洛谷

题目描述

你玩过“拉灯”游戏吗?

25 盏灯排成一个 5×5 的方形。

每一个灯都有一个开关,游戏者可以改变它的状态。

每一步,游戏者可以改变某一个灯的状态。

游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字 1 表示一盏开着的灯,用数字 0 表示关着的灯。

下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。

输入格式

第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。

以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。

每组数据描述了一个游戏的初始状态。

各组数据间用一个空行分隔。

输出格式

一共输出 n 行数据,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。

对于某一个游戏初始状态,若 6 步以内无法使所有灯变亮,则输出 -1

输入输出样例

输入 #1复制

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

输出 #1复制

3
2
-1

说明/提示

测试数据满足 0

状压+bfs:

#include
using namespace std;
vector> b(5, vector(5, 1));
vector dp(1 << 25, -1);
int trans(vector> a)//状态压缩,将二维数组压缩成二进制数
{
    int res = 0;
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++)
            if (a[i][j] == 1)
                res |= (1 << (i * 5 + j));
    return res;
}
int change(int now, int i)//将目标地点上下左右中都改变
{
    now ^= (1 << i); 
    if (i % 5 != 0)
        now ^= (1 << (i - 1));
    if ((i + 1) % 5 != 0)
        now ^= (1 << (i + 1));
    if (i >= 5)
        now ^= (1 << (i - 5));
    if (i < 20)
        now ^= (1 << (i + 5));
    return now;
}

void init()//初始化,推出所有可能的情况并存入dp中
{
    int zip = trans(b);
    queue q;//使用队列来完成bfs
    q.push(zip);
    dp[zip] = 0;
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        if (dp[now] > 6)
            break;
        for (int i = 0; i < 25; i++)
        {
            int next = change(now, i);
            if (dp[next] == -1)
            {
                dp[next] = dp[now] + 1;
                q.push(next);
            }
        }
    }
}
void solve()//从dp中检查数据并输出
{

    vector> a(5, vector(5));
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            char c;
            cin >> c;
            a[i][j]=c-'0';
        }
    }  
    int ans=trans(a);
    cout<<(dp[ans]>6?-1:dp[ans])<> t;
    init();
    while (t--)
    {
        solve();
    }
    return 0;
}

然后我还想着写了个双向bfs的将复杂度从25的6次方压缩到25的3次方。

但是这样的话反而又tle了。是因为之前初始化一次数据够每组数据使用,但是双向bfs之后要多次进行数据处理。

代码如下

#include
using namespace std;
vector> b(5, vector(5, 1));

int trans(vector> a)
{
    int res = 0;
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++)
            if (a[i][j] == 1)
                res |= (1 << (i * 5 + j));
    return res;
}
int change(int now, int i)
{
    now ^= (1 << i);
    if (i % 5 != 0)
        now ^= (1 << (i - 1));
    if ((i + 1) % 5 != 0)
        now ^= (1 << (i + 1));
    if (i >= 5)
        now ^= (1 << (i - 5));
    if (i < 20)
        now ^= (1 << (i + 5));
    return now;
}
int shuang(int now)
{
    vector dp1(1 << 25, -1), dp2(1 << 25, -1);
    int ans = 0;
    queue q1, q2;
    q1.push(now);
    dp1[now] = 0;
    int target = trans(b);
    if(now==target)
        return 0;
    q2.push(target);
    dp2[target] = 0;
    while (!q1.empty() && !q2.empty())
    {
        if (q1.size() > q2.size())
        {
            int cur = q2.front();
            q2.pop();
            if(dp2[cur]+dp1[q1.front()]>6)
                return -1;
            for (int i = 0; i < 25; i++)
            {
                int next = change(cur, i);
                if (dp2[next] == -1)
                {
                    dp2[next] = dp2[cur] + 1;
                    q2.push(next);
                }
                if (dp1[next] != -1)
                {
                    ans = dp1[next] + dp2[next];
                    return ans;
                }
            }
        }
        else
        {
            int cur = q1.front();
            q1.pop();
            if(dp1[cur]+dp2[q2.front()]>6)
                return -1;
            for (int i = 0; i < 25; i++)
            {
                int next = change(cur, i);
                if (dp1[next] == -1)
                {
                    dp1[next] = dp1[cur] + 1;
                    q1.push(next);
                }
                if (dp2[next] != -1)
                {
                    ans = dp1[next] + dp2[next];
                    return ans;
                }
            }
        }
    }

}
void solve()
{
    vector> a(5, vector(5));
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            char c;
            cin >> c;
            a[i][j] = c - '0';
        }
    }
    int ans = trans(a);
    int res = shuang(ans);
    if (res >6)
        res = -1;
    cout << res << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

你可能感兴趣的:(每日一题,算法)