2013 Asia Hangzhou Regional Contest--hdu4770Lights Against Dudely(DLX)

11 篇文章 0 订阅

题目请戳这里

题目大意:给一个n*m的格子,每个格子可以是'#'或者是'.'。'.'的数量不超过15个。现在要在'.'的位置放一种灯,假设放在(x,y)的位置。那么这盏灯只能点亮(x,y),(x-1,y),(x,y + 1),这3个位置。现在有一盏灯比较特殊,可以绕(x,y)旋转0度,90度,180度或者270度。现在给这样n*m的格局,求最少放置多少灯能覆盖所有的'.'。每个'.'的位置只能放置一盏灯,但是'.'的位置可以重复照射。'#'的位置不能被照射。

题目分析:第一反应是重复覆盖问题。不过今天不在状态,一开始想复杂了。

一共有15盏灯,每盏灯有4个姿势,那么一共就是15*4行,一共要覆盖15盏灯,那么就是15列。

枚举每个点的4个姿势,建图。然后再枚举那盏能旋转的灯。然后直接跑DLX重复覆盖即可。

详情请见代码:

#include <iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 201;
const int M = 910;

char map[N][N];
int vnum,n,m,num,cur;
short int s[M],h[M],u[M],d[M],l[M],r[M],col[M],row[M];
bool flag[N];
int dir[4][2] = {-1,0,0,1,1,0,0,-1};
int pt[N][2];
int ans,sp;
void init()
{
    memset(h,0,sizeof(h));
    memset(s,0,sizeof(s));
    for(int i = 0;i <= vnum;i ++)
    {
        u[i] = d[i] = i;
        l[i] = (i + vnum)%(vnum + 1);
        r[i] = (i + 1)%(vnum + 1);
    }
    num  = vnum  + 1;
}
void add(int i,int j)
{
    if(h[i])
    {
        r[num] = h[i];
        l[num] = l[h[i]];
        r[l[num]] = l[r[num]] = num;
    }
    else
        h[i] = l[num] = r[num] = num;
    s[j] ++;
    u[num] = u[j];
    d[num] = j;
    d[u[num]] = num;
    u[j] = num;
    col[num] = j;
    row[num] = i;
    num ++;
}
void remove(int c)
{
    for(int i = d[c];i != c;i = d[i])
    {
        l[r[i]] = l[i];
        r[l[i]] = r[i];
        s[col[i]] --;
    }
}
void resume(int c)
{
    for(int i = u[c];i != c;i = u[i])
        l[r[i]] = r[l[i]] = i,s[col[i]] ++;
}
void build()
{
    int i,j;
    for(i = 0;i < vnum;i ++)
    {
        for(j = 0;j < 4;j ++)
        {
            int tx = pt[i][0] + dir[j][0];
            int ty = pt[i][1] + dir[j][1];
            int ttx = pt[i][0] + dir[(j + 1)%4][0];
            int tty = pt[i][1] + dir[(j + 1)%4][1];
            int x = pt[i][0];
            int y = pt[i][1];
            if(map[tx][ty] == '#' || map[ttx][tty] == '#')
                continue;
            if(tx >= 1 && tx <= n && ty >= 1 && ty <= m)
                add(i * 4 + j,map[tx][ty]);
            if(ttx >= 1 && ttx <= n && tty >= 1 && tty <= m)
                add(i * 4 + j,map[ttx][tty]);
            add(i * 4 + j,map[x][y]);
        }
    }
}
void dfs(int k)
{
    if(!r[0])
    {
        cur = min(cur,k);
        return;
    }
    int i,j,c,mn = N;
    for(i = r[0];i;i = r[i])
    {
        if(s[i] < mn)
        {
            mn = s[i];
            c = i;
        }
    }
    for(i = d[c];i != c;i = d[i])
    {
        if(flag[row[i]/4] == true)
            continue;
        if(row[i]%4 && row[i]/4 != sp)
            continue;
        remove(i);
        flag[row[i]/4] = true;
        for(j = r[i];j != i;j = r[j])
            remove(j);
        dfs(k + 1);
        for(j = l[i];j != i;j = l[j])
            resume(j);
        flag[row[i]/4] = false;
        resume(i);
    }
}
void dance()
{
    ans = M;
    int i;
    for(i = 0;i < vnum;i ++)
    {
         sp = i;
         cur = M;
         memset(flag,false,sizeof(flag));
         dfs(0);
         ans = min(ans,cur);
    }
    if(ans == M)
        ans = -1;
    printf("%d\n",ans);
}
int main()
{
    int i,j;
    char ss[300];
    while(scanf("%d%d",&n,&m),(n + m))
    {
        vnum = 0;
        gets(ss);
        memset(map,0,sizeof(map));
        for(i = 1;i <= n;i ++)
        {
            for(j = 1;j <= m;j ++)
            {
                map[i][j] = getchar();
                if(map[i][j] == '.')
                {
                    pt[vnum][0] = i;
                    pt[vnum][1] = j;
                    vnum ++;
                    map[i][j] = vnum;
                }
            }
            getchar();
        }
        if(vnum == 0)
        {
            printf("0\n");
            continue;
        }
        init();
        build();
        dance();
    }
    return 0;
}
//0MS	296K
/*
2 3
#..
..#
3 3
###
#.#
###
4 4
#...
....
....
....
3 3
#.#
...
#.#
4 4
####
#..#
#..#
####
4 7
#######
##.#.##
#..#..#
#######
2 2
#.
##
3 3
#.#
...
..#
0 0
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值