Problem 1686 神龙的难题
Accept: 661 Submit: 1983
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
Input
Output
Sample Input
4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
2 2
4 4
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
2 2
Sample Output
41
Source
FOJ月赛-2009年2月- TimeLoop
DancingLinksX重复覆盖的题目
题目意思&……
做法
龙的一次攻击作为行
格点作为列
龙对应的一次攻击能打到的位置为1
求重复覆盖的最小次数
这里特别的把没有生物的格点直接去掉了
这样就少了很多列
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MN=20*20;
const int MM=20*20;
const int MNN=MN*MM+MM; //最大点数
struct DLX
{
int n,m,si;//n行数m列数si目前有的节点数
//十字链表组成部分
int U[MNN],D[MNN],L[MNN],R[MNN],Row[MNN],Col[MNN];
//第i个结点的U向上指针D下L左R右,所在位置Row行Col列
int H[MN],S[MM]; //记录行的选择情况和列的覆盖情况
int ansd,ans[MN];
void init(int _n,int _m) //初始化空表
{
n=_n;
m=_m;
for(int i=0;i<=m;i++) //初始化第一横行(表头)
{
S[i]=0;
U[i]=D[i]=i; //目前纵向的链是空的
L[i]=i-1;
R[i]=i+1; //横向的连起来
}
R[m]=0;L[0]=m;
si=m; //目前用了前0~m个结点
for(int i=1;i<=n;i++)
H[i]=-1;
}
void link(int r,int c) //插入点(r,c)
{
++S[Col[++si]=c]; //si++;Col[si]=c;S[c]++;
Row[si]=r;
D[si]=D[c];
U[D[c]]=si;
U[si]=c;
D[c]=si;
if(H[r]<0)
H[r]=L[si]=R[si]=si;
else
{
R[si]=R[H[r]];
L[R[H[r]]]=si;
L[si]=H[r];
R[H[r]]=si;
}
}
void remove(int c)
{
for(int i=D[c];i!= c;i= D[i])
L[R[i]]=L[i],R[L[i]]=R[i];
}
void resume(int c)
{
for(int i=U[c];i!= c;i=U[i])
L[R[i]]=R[L[i]]=i;
}
bool v[MNN];
int h() //估值
{
int ret=0;
for(int c=R[0];c!=0;c=R[c])
v[c]=1;
for(int c=R[0];c!=0;c=R[c])
if(v[c])
{
ret++;
v[c]=0;
for(int i=D[c];i!=c;i=D[i])
for(int j=R[i];j!=i;j=R[j])
v[Col[j]]=0;
}
return ret;
}
void dance(int d)
{
if(d+h()>=ansd) //利用A*优化
return;
if(R[0]==0)
{
ansd=d;
return;
}
int c=R[0];
for(int i=R[0];i!=0;i=R[i])
if(S[i]<S[c])
c=i;
for(int i=D[c];i!=c;i=D[i])
{
remove(i);
for(int j=R[i];j!=i;j=R[j])
remove(j);
dance(d+1);
for(int j = L[i];j != i;j = L[j])
resume(j);
resume(i);
}
}
}dlx;
const int M=20;
int ti[M][M];
int p[M][M];
int main()
{
int n,m,n1,m1;
while(scanf("%d%d",&n,&m)!=EOF)
{
int pp=0; //去掉多余的列
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&ti[i][j]);
if(ti[i][j])
p[i][j]=++pp;
}
scanf("%d%d",&n1,&m1);
dlx.init((n-n1+1)*(m-m1+1),pp);
for(int ii=1;ii<=n-n1+1;ii++)
for(int jj=1;jj<=m-m1+1;jj++)
{
int tmp=(ii-1)*(m-m1+1)+jj;
for(int i=0;i<n1;i++)
for(int j=0;j<m1;j++)
if(ti[ii+i][jj+j])
dlx.link(tmp,p[ii+i][jj+j]);
}
dlx.ansd=1e9;
dlx.dance(0);
printf("%d\n",dlx.ansd);//一定存在最多次数
}
return 0;
}