2013杭州站A - Lights Against Dudely(状态模拟)

本文解决了一个特定的照明问题,即如何使用最少数量的灯照亮一个由'.', '#'组成的n*m矩阵,其中'.'代表需要被照亮的位置,'#'代表障碍物。通过状态压缩和枚举策略,文章详细介绍了如何找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址

题目大意:给出一个n*m的字符矩阵,'.'代表需要灯光照亮(最多15个),'#'代表不能被照亮,只能在’.‘的地方放灯,一个普通的灯可以照亮其本身+上面+右面,一个特别的灯可以旋转0度,90度,180度,270度,并照亮其相对的本身+上面+右面,仅有一盏特别的灯,其余都是普通灯,求将所有'.'照亮所需要的最少的灯的数量,若不能照亮所有的’.‘,输出-1,若没有'.',输出0

解题思路:因为需要被照亮的地方最多15个,放灯的位置用1表示,不放灯的地方用0表示,表示成一种状态,对于每种状态,枚举某一位置(j)为特殊灯,对状态中其余所有1的位置都看一下是否满足普通灯的条件(上面和右面不能是’#‘),若不能满足,则j位置不能放特殊灯,若能放特殊灯,则检测是否所有’.‘的位置都被照亮了,若都被照亮则将该种状态放灯的数目记录下来,比较求最小。另,在处理过程中,需要用一个数组将点的序号与其坐标相对应

#include <bits/stdc++.h>

using namespace std;

char g[220][220];
pair<int,int> p[20];
int a[220][220];
int d[220][220];
bool f[20];
const int INF = 0x3f3f3f3f;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m) == 2)
    {
        if(n == 0 && m == 0)break;
        for(int i = 1;i <= n;i++)
            scanf("%s",g[i]+1);
        int cnt = 0;
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++)
                if(g[i][j] == '.')
                {
                    p[cnt] = make_pair(i,j);
                    d[i][j] = cnt++;
                }
        if(cnt == 0)
        {
            printf("0\n");
            continue;
        }
        int ans = INF;
        int tot = (1<<cnt);
        for(int i = 0;i < tot;i++)
        {
            for(int j = 0;j < cnt;j++)
                if(i & (1<<j))///对于i状态的第j个位置放灯
                {
                    for(int k = 0; k < 4;k++)///的4个方向
                    {
                        for(int tt = 0;tt < cnt;tt++)///对于cnt个位置,先初始化不放灯
                            f[tt] = false;
                        bool flag = true;
                        for(int t = 0;t < cnt;t++) ///对于第t个位置
                        {
                            if(i & (1<<t)) ///若i状态中的每个放灯位置
                                if(t != j)///若t位不是j位,看上右是否能放灯
                                {
                                    int x = p[t].first;///t位坐标
                                    int y = p[t].second;
                                    f[d[x][y]] = true;///标记放灯
                                    if(x-1 > 0)
                                    {
                                        if(g[x-1][y] == '#')    flag = false;///上边不能放灯
                                        else f[d[x-1][y]] = true; ///上边位置放灯
                                    }
                                    if(y+1 <= m)
                                    {
                                        if(g[x][y+1] == '#')flag = false;///右边不能放灯
                                        else f[d[x][y+1]] = true;///右边放灯
                                    }
                                    if(!flag) break;///若某个放灯位置的右|上不符合放灯,则停止枚举
                                }
                        }
                        if(!flag) continue;///若有某个点不能放灯,则不对其4个方向继续搜索
                        int x = p[j].first;
                        int y = p[j].second;
                        f[d[x][y]] = true; ///否则对j位置进行旋转
                        if(k == 0)  ///0度
                        {
                            if(x-1 > 0) ///上方
                            {
                                if(g[x-1][y] == '#') flag = false;
                                else f[d[x-1][y]] = true;
                            }
                            if(y+1 <= m) ///右方
                            {
                                if(g[x][y+1] == '#')flag = false;
                                else f[d[x][y+1]] = true;
                            }
                        }
                        else if(k == 1)///90度
                        {
                            if(x+1 <= n)///下方
                            {
                                if(g[x+1][y] == '#')flag = false;
                                else f[d[x+1][y]] = true;
                            }
                            if(y+1 <= m)///右方
                            {
                                if(g[x][y+1] == '#')flag = false;
                                else f[d[x][y+1]] = true;
                            }
                        }
                        else if(k == 2)///180度
                        {
                            if(x+1 <= n)///下方
                            {
                                if(g[x+1][y] == '#')flag = false;
                                else f[d[x+1][y]] = true;
                            }
                            if(y-1 > 0)///左方
                            {
                                if(g[x][y-1] == '#')flag = false;
                                else f[d[x][y-1]] = true;
                            }
                        }
                        else///270度
                        {
                            if(x-1 > 0)///上方
                            {
                                if(g[x-1][y] == '#')flag = false;
                                else f[d[x-1][y]] = true;
                            }
                            if(y-1 > 0)///左方
                            {
                                if(g[x][y-1] == '#')flag = false;
                                else f[d[x][y-1]] = true;
                            }
                        }
                        if(!flag) continue; ///若有某个方向不能放,则放弃j位置旋转
                        for(int t = 0;t < cnt;t++)///对于cnt个位置,若最后一个位置不能放灯
                            if(f[t] == false)
                            {
                                flag = false;break;
                            }
                        if(!flag)continue; ///放弃i状态
                        int num = 0;
                        for(int t = 0;t < cnt;t++) ///对于i状态,记录等的个数
                            if(i & (1<<t))
                                num++;
                        ans = min(ans,num);
                    }
                }
        }
        if(ans == INF)ans = -1;
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值