FZU - 1686 神龙的难题 重复覆盖

题目:这是个剑与魔法的世界.英雄和魔物同在,动荡和安定并存.但总的来说,库尔特王国是个安宁的国家,人民安居乐业,魔物也比较少.但是.总有一些魔物不时会进入城市附近,干扰人民的生活.就要有一些人出来守护居民们不被魔物侵害.魔法使艾米莉就是这样的一个人.她骑着她的坐骑,神龙米格拉一起消灭干扰人类生存的魔物,维护王国的安定.艾米莉希望能够在损伤最小的前提下完成任务.每次战斗前,她都用时间停止魔法停住时间,然后米格拉他就可以发出火球烧死敌人.米格拉想知道,他如何以最快的速度消灭敌人,减轻艾米莉的负担.

input:数据有多组,你要处理到EOF为止.每组数据第一行有两个数,n,m,(1<=n,m<=15)表示这次任务的地区范围. 然后接下来有n行,每行m个整数,如为1表示该点有怪物,为0表示该点无怪物.然后接下一行有两个整数,n1,m1 (n1<=n,m1<=m)分别表示米格拉一次能攻击的行,列数(行列不能互换),假设米格拉一单位时间能发出一个火球,所有怪物都可一击必杀.

output:输出一行,一个整数,表示米格拉消灭所有魔物的最短时间.


代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define PI acos(-1.0)
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f

int limit;//重复覆盖时使用,行数选择的最大数目
struct DLX {
	const static int ROW = 1003, COL = 1003, SIZE = ROW * COL;
	int L[SIZE], R[SIZE], U[SIZE], D[SIZE];  //模拟指针
	int col[SIZE], row[SIZE]; //所在列 所在行
	int visn, visited[COL]; //用于估价函数
	int sel[ROW], seln; //选择的行
	int sz[COL]; //列元素数
	int total/*节点编号*/, H[ROW];
	void init(int clen) { //初始化列头指针
		for(int i = 0; i <= clen; ++i) {
			L[i] = i - 1; R[i] = i + 1;
			U[i] = D[i] = i; sz[i] = 0;
		}
		for(int i=0;i<ROW;i++) H[i]=-1;
		for(int i=0;i<COL;i++) visited[i]=0; visn = 0; //用于重复覆盖的A*剪枝
		L[0] = clen; R[clen] = 0; total = clen + 1;
	}
	void link(int r, int c) {//行列都是从下标1开始
		U[total] = c; D[total] = D[c];
		U[D[c]] = total; D[c] = total;
		if(H[r] < 0) H[r] = L[total] = R[total] = total;
		else {
			L[total] = H[r]; R[total] = R[H[r]];
			L[R[H[r]]] = total; R[H[r]] = total;
		}
		sz[c]++; col[total] = c; row[total++] = r;
	}
//-------------------- 精确覆盖 -------------------------
	void remove(const int &c) { //删除c列上所有1元素所在的行
		L[R[c]] = L[c]; R[L[c]] = R[c];
		for(int i = D[c]; i != c; i = D[i])
			for(int j = R[i]; j != i; j = R[j])
				U[D[j]] = U[j], D[U[j]] = D[j], --sz[col[j]];
	}
	void resume(const int &c) { //恢复c列上所有1元素所在的行
		R[L[c]] = L[R[c]] = c;
		for(int i = U[c]; i != c; i = U[i])
			for(int j = L[i]; j != i; j = L[j])
				++sz[col[U[D[j]] = D[U[j]] = j]];
	}
	bool dance(int now) {
		if(R[0] == 0) {
			seln = now;
			return true;
		}
		int c=R[0], i, j;
		for(i = R[0]; i != 0; i = R[i]) //选择元素最少的列c
			if(sz[c] > sz[i]) c = i;
		remove(c);
		for(i = D[c]; i != c; i = D[i]) { //删除与c相连的行i
			for(j = R[i]; j != i; j = R[j]) //删除行元素所在的列j
				remove(col[j]);
			sel[now] = row[i]; //选择此行 保存行号
			if(dance(now + 1)) { //对于不同的题 这个地方常常需要改动
				return true;
			}
			for(j = L[i]; j != i; j = L[j])
				resume(col[j]);
		}
		resume(c);
		return false;
	}
//-------------------- 重复覆盖 -------------------------
	void _remove(const int &c) {
		for(int i = D[c]; i != c; i = D[i])
			L[R[i]] = L[i], R[L[i]] = R[i];
	}
	void _resume(const int &c) {
		for(int i = U[c]; i != c; i = U[i])
			L[R[i]] = R[L[i]] = i;
	}
	int Astar(){ //估价函数
		int res = 0; ++visn;
		for(int i = R[0]; i != 0; i = R[i]) {
			if(visited[i] != visn) {
				++res; visited[i] = visn;
				for(int j = D[i]; j != i; j = D[j])
					for(int k = R[j]; k != j; k = R[k])
						visited[col[k]] = visn;
			}
		} return res;
	}
	void _dance(int now) {
	    if(now + Astar() >= limit){
            return;
	    }
		if(R[0] == 0){
            limit=min(limit,now);
            return;
		}
		int c=R[0], i, j;
		for(i = R[0]; i != 0; i = R[i]) //选择元素最少的列c
			if(sz[c] > sz[i]) c = i;
		for(i = D[c]; i != c; i = D[i]) {
			_remove(i);
			for(j = R[i]; j != i; j = R[j])
				_remove(j);
			_dance(now + 1);
			for(j = L[i]; j != i; j = L[j])
				_resume(j);
			_resume(i);
		}
	}
}dl;
int arr[20][20];
int main(){

    int n,m,n1,m1;
    while(~scanf("%d%d",&n,&m)){
        int r=1,c=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&arr[i][j]);
                if(arr[i][j])
                    arr[i][j]=++c;
            }
        dl.init(c);
        scanf("%d%d",&n1,&m1);
        for(int i=1;i+n1-1<=n;i++)
            for(int j=1;j+m1-1<=m;j++){
                int flag=0;
                for(int k=i;k<=i+n1-1;k++)
                    for(int t=j;t<=j+m1-1;t++){
                        if(arr[k][t]>0){
                            dl.link(r,arr[k][t]);
                            flag=1;
                        }
                    }
                if(flag) r++;
            }
        limit=INF;
        dl._dance(0);
        printf("%d\n",limit);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值