poj2870Light Up(迭代加深搜索)

Light Up
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 754 Accepted: 291

Description

Light Up is a puzzle set in a rectangular board divided in smaller squares. Some squares in the board are ``empty'' (white squares the figure below), some squares are ``barriers'' (dark squares in the figure below). A barrier square may have an integer number i associated to it (0 <= i <= 4). 

 
Figure 2: (a) Puzzle with 6 rows, 7 columns and 7 barriers; (b) a solution to the puzzle.


In this puzzle the goal is to ``light up'' all the empty squares by placing lamps in some of them (lamps are depicted as circles in the figure). Each lamp illuminates the square it is on, plus all squares in line with it, horizontally or vertically, up to a barrier square or the board end. 

A winning configuration satisfies the following conditions:
  • all empty squares must be lit;
  • no lamp may be lit by another lamp;
  • all numbered barrier squares must have exactly that number of lamps adjacent to them (in the four squares above, below, and to the side);
  • non-numbered barrier squares may have any number of lamps adjacent to them.


You must write a program to determine the smallest number of lamps that are needed to reach a winning configuration.

Input

The input contains several test cases. The first line of a test case contains two integers N, M indicating respectively the number of rows and the number of columns of the board (1 <= N <= 7, 1 <= M <= 7). The second line contains one integer B indicating the number of barrier squares (0 <= B <= N × M). Each of the next B lines describe a barrier, containing three integers R, C and K, representing respectively the row number (1 <= R <= N), the column number (1 <= C <= M) and the barrier number (-1 <= K <= 4); K = -1 means the barrier is unnumbered. The end of input is indicated by N = M = 0.

Output

For each test case in the input your program must produce one line of output, containing either an integer indicating the smallest number of lamps needed to reach a winning configuration, in case such a configuration exists, or the words `No solution'.

Sample Input

2 2
0
2 2
1
2 2 1
6 7
7
2 3 -1
3 3 0
4 2 1
5 4 3
5 6 2
1 7 -1
6 5 -1
0 0

Sample Output

2
No solution
8

Source


这几天被搜索虐出翔了啊有木有,这题也能卡这么久。。。。
题目大意:给一个地图,地图最大7*7,里面有空地,有障碍,障碍有标号,标号-1~4,-1表示该障碍附近能放任意灯泡,其他的障碍周围只能恰好放与障碍标号相同数量的灯泡。一个灯泡能点亮该行该列的所有空地(除去障碍物遮挡)。求最少的放置灯泡数能照亮地图上所有的空地。注意已经被点亮的地方不能放灯泡。
题目分析:搜索。。。因为地图太小了,直接爆搜+剪枝也可以过。一开始也准备这样写的,写完超长的代码,一跑样例只过了2个,调试也很没心情。索性重写。重写的时候想了下,既然要保证有编号的障碍周围放固定数目的灯泡,与其盲目搜索,还不如先满足那些有编号的障碍物。于是想出了迭代加深搜索,因为地图太小,总共也不超过49步。也没想出好的启发函数,只能用朴素的迭代加深搜索。首先将有编号的障碍物找出来,先对障碍物进行一次dfs,找出满足所有有编号的障碍物的要求的一个可行解,然后再进一步Dfs,将剩下的没有点亮的空地点亮。
一共用到了2个数组,flag[i][j]=2表示ij位置是障碍,flag[i][j] = 3表示ij位置是灯泡,flag[i][j]= 0表示ij位置什么也没有。cover[i][j]表示ij位置是否被点亮,0表示未点亮,>0表示被点亮,因为同一个地方可能被照射多次,所以被点亮的点ij直接执行cover[i][j] ++。
最后还是被discuss里的一组数据卡住了,后来想了想,所有迭代加深的起点都是0,所以导致一开始的搜索都是明显不合法的,应该剪掉,所以迭代的深度的初始值用h()求出来。具体做法比较猥琐,就是扫描一遍初始地图,如果发现某个空地周围都是障碍,那么这个点至少要一个灯,统计出所有这样的情况,作为迭代的初始深度,然后就秒过了。。。
代码依然又臭又长,将就着看看吧:D
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
const int N = 9;
bool ok;
int flag[N][N];
int cover[N][N];
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
int n,m,b,ans;
struct node
{
    int x,y,val;
}barrier[50];
int cmp(struct node a,struct node b)
{
    return a.val < b.val;
}
int nextint()
{
    int ret;
    char c;
    bool sig = false;
    while(isspace(c = getchar()))
        ;
    if(c == '-')
    {
        sig = true;
        c = getchar();
    }
    ret = c - '0';
    while((c = getchar()) >= '0' && c <= '9')
        ret = ret * 10 + c - '0';
    return sig?-ret:ret;
}

bool jud(int x,int y)
{
    return !(x >= 1 && y >= 1 && x <= n && y <= m);
}

int h()
{
    int i,j;
    int ret = 0;
    for(i = 1;i <= n;i ++)
    {
        for(j = 1;j <= m;j ++)
        {
            if(!cover[i][j] && flag[i][j] != 2)
            {
                if((flag[i - 1][j] == 2 || jud(i - 1,j))
                   && (flag[i + 1][j] == 2 || jud(i + 1,j))
                   && (flag[i][j - 1] == 2 || jud(i,j - 1))
                    && (flag[i][j + 1] == 2 || jud(i,j + 1)))
                    ret ++;
            }
        }
    }
    return ret;
}

int put(int x,int y)
{
    int ret = 1;
    int i,j;
    i = x - 1;
    j = y;
    while(i > 0 && flag[i][j] != 2)
    {
        cover[i][j] ++;
        if(cover[i][j] == 1)
            ret ++;
        i --;
    }
    i = x + 1;
    j = y;
    while(i <= n && flag[i][j] != 2)
    {
        cover[i][j] ++;
        if(cover[i][j] == 1)
            ret ++;
        i ++;
    }
    i = x;
    j = y - 1;
    while(j > 0 && flag[i][j] != 2)
    {
        cover[i][j] ++;
        if(cover[i][j] == 1)
            ret ++;
        j --;
    }
    j = y + 1;
    while(j <= m && flag[i][j] != 2)
    {
        cover[i][j] ++;
        if(cover[i][j] == 1)
            ret ++;
        j ++;
    }
    return ret;
}

void deput(int x,int y)
{
    int ret = 1;
    int i,j;
    i = x - 1;
    j = y;
    while(i > 0 && flag[i][j] != 2)
    {
        cover[i][j] --;
        i --;
    }
    i = x + 1;
    while(i <= n && flag[i][j] != 2)
    {
        cover[i][j] --;
        i ++;
    }
    i = x;
    j = y - 1;
    while(j > 0 && flag[i][j] != 2)
    {
        cover[i][j] --;
        j --;
    }
    j = y + 1;
    while(j <= m && flag[i][j] != 2)
    {
        cover[i][j] --;
        j ++;
    }
}


int check()//0 irllegal 1 ok 2 right
{
    int i,j;
    int tmp;
    int ret = 2;
    for(i = 0;i < b;i ++)
    {
        if(barrier[i].val == -1)
            continue;
        tmp = 0;
        for(j = 0;j < 4;j ++)
        {
            int tx = barrier[i].x + dx[j];
            int ty = barrier[i].y + dy[j];
            if(tx < 1 || ty < 1 || tx > n || ty > m)
                continue;
            if(flag[tx][ty] == 3)
                tmp ++;
        }
        if(tmp > barrier[i].val)
            return 0;
        if(tmp != barrier[i].val)
            ret = 1;
    }
    return ret;
}

void print()
{
    int i,j;
    for(i = 1;i <= n;i ++)
    {
        for(j = 1;j <= m;j ++)
            printf("%d ",flag[i][j]);
        putchar(10);
    }
    putchar(10);
    for(i = 1;i <= n;i ++)
    {
        for(j = 1;j <= m;j ++)
            printf("%d ",cover[i][j]);
        putchar(10);
    }
}

void Dfs(int x,int y,int cnt,int ca)
{
    if(ca > ans)
        return;
    int i,j;
    if(ok)
        return;
    if(cnt == m * n - b)
    {
        ok = true;
        return;
    }
    for(i = x;i <= n;i ++)
    {
        for(j = 1;j <= m;j ++)
        {
            if(flag[i][j] == 0 && cover[i][j] == 0)
            {
                flag[i][j] = 3;
                if(check() != 2)
                {
                    flag[i][j] = 0;
                    continue;
                }
                int tp = put(i,j);
                Dfs(i,j,cnt + tp,ca + 1);
                deput(i,j);
                flag[i][j] = 0;
            }
        }
    }
}

void dfs(int id,int cnt,int ca)
{
    if(ca > ans)
        return;
    if(ok)
        return;
    if(id == b)
    {
        if(check() == 2)//right
        {
            Dfs(1,1,cnt,ca);
        }
        return;
    }
    int i,j;
    for(i = j = 0;i < 4;i ++)
    {
        if(flag[barrier[id].x + dx[i]][barrier[id].y + dy[i]] == 3)
            j ++;
    }
    if(j > barrier[id].val)
        return;
    else
    {
        if(j == barrier[id].val)
            dfs(id + 1,cnt,ca);
        else
        {
            for(i = 0;i < 4;i ++)
            {
                int tx = barrier[id].x + dx[i];
                int ty = barrier[id].y + dy[i];
                if(tx < 1 || ty < 1 || tx > n || ty > m)
                    continue;
                if(flag[tx][ty] == 0 && cover[tx][ty] == 0)
                {
                    flag[tx][ty] = 3;
                    if(check() == 0)
                    {
                        flag[tx][ty] = 0;
                        continue;
                    }
                    int tp = put(tx,ty);
                    dfs(id,cnt + tp,ca + 1);
                    deput(tx,ty);
                    flag[tx][ty] = 0;
                }
            }
        }
    }
}

int main()
{
    int i,j;
    while(n = nextint())
    {
        m = nextint();
        if(m + n == 0)
            break;
        b = nextint();
        memset(flag,0,sizeof(flag));
        memset(cover,0,sizeof(cover));
        for(i = 0;i < b;i ++)
        {
            barrier[i].x = nextint();
            barrier[i].y = nextint();
            barrier[i].val = nextint();
            flag[barrier[i].x][barrier[i].y] = 2;//
        }
        sort(barrier,barrier + b,cmp);

        ok = false;
        for(i = 0;i < b;i ++)
            if(barrier[i].val > 0)
                break;
        ans = h();
        //print();
        while(1)
        {
            if(ok || ans > m * n - b)
                break;
            dfs(i,0,0);
            if(ok)
                break;
            ans ++;
        }
        if(ok == false)
            printf("No solution\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}
//168K	94MS
/*
2 2
4
1 1 -1
1 2 -1
2 1 -1
2 2 -1
2 2
0
2 2
1
2 2 1
6 7
7
2 3 -1
3 3 0
4 2 1
5 4 3
5 6 2
1 7 -1
6 5 -1
7 7
24
1 2 -1
1 4 -1
1 6 -1
2 1 -1
2 3 -1
2 5 -1
2 7 -1
3 2 -1
3 4 -1
3 6 -1
4 1 -1
4 3 -1
4 5 -1
4 7 -1
5 2 -1
5 4 -1
5 6 -1
6 1 -1
6 3 -1
6 5 -1
6 7 -1
7 2 -1
7 4 -1
7 6 -1

*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值