hdu 2295 DLX重复覆盖

本文介绍了一种解决跳棋问题的有效算法,通过构建一种特殊的矩阵结构并运用回溯搜索结合估价函数来找到最优解。该算法可以高效地解决大规模跳棋匹配问题,并通过二分查找进一步优化解的搜索过程。

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

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <ctime>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef int ll;
#define INF 1e9
#define MAXN 10000
#define MAXM 100
const int maxn = 1000005;
//#define mod 1000000007
#define eps 1e-7
#define pi 3.1415926535897932384626433
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define scan(n) scanf("%d",&n)
#define scanll(n) scanf("%I64d",&n)
#define scan2(n,m) scanf("%d%d",&n,&m)
#define scans(s) scanf("%s",s);
#define ini(a) memset(a,0,sizeof(a))
#define out(n) printf("%d\n",n)
//ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b);}
using namespace std;
#define N 60
#define V N*N
int L[V],R[V];//记录左右方向的双向链表
int U[V],D[V];//记录上下方向的双向链表
int C[V];//指向其列指针头的地址
int H[N];//行指针头
int S[N];//记录列链表中节点的总数
int size,n,m,K,ak;
int visit[N];
void Link(int r,int c)
{
	S[c]++;C[size]=c;
	U[size]=U[c];D[U[c]]=size;
	D[size]=c;U[c]=size;
	if(H[r]==-1) H[r]=L[size]=R[size]=size;
	else
	{
		L[size]=L[H[r]];R[L[H[r]]]=size;
		R[size]=H[r];L[H[r]]=size;
	}
	size++;
}
void remove(int Size)
{
	int j; //重复覆盖不删除行
	for(j=D[Size];j!=Size;j=D[j])
		L[R[j]]=L[j],R[L[j]]=R[j];
}
void resume(int Size)
{
	int j;
	for(j=D[Size];j!=Size;j=D[j])
		L[R[j]]=R[L[j]]=j;
}
int h()
{
	int count=0,i,j,k; //估价函数
	memset(visit,0,sizeof(visit));
	for(i=R[0];i;i=R[i])
	{
		if(visit[i]) continue;
		count++;
		for(j=D[i];j!=i;j=D[j])
		{
			for(k=R[j];k!=j;k=R[k])
				visit[C[k]]=1;
		}
	}
	return count;
}
bool Dance(int k)
{ //重复覆盖一般需要暴搜,然后用估价函数剪枝
	int i,j,min,c; 
	if(k + h() > K || k+h() >= ak) return false; //k表示已选择的点,h()为估计至少需要的点,ak为当前答案
	if(!R[0])
	{
		if(k<ak) ak=k;
		return true;
	}
	for(min=N,i=R[0];i;i=R[i])
		if(min>S[i]) min=S[i],c=i;
	for(i=D[c];i!=c;i=D[i])
	{
		remove(i); //这个写里面,因为不一定删i所在的行,
		for(j=R[i];j!=i;j=R[j]) //循环删除该行剩下的,总的来说就是删了整行
			remove(j);
		if(Dance(k+1)) return true;
		for(j=R[i];j!=i;j=R[j])
			resume(j);
		resume(i);
	}
	return false;
}
void init(int n)
{
	for(int i=0;i<=n;i++)
	{
		S[i]=0;
		U[i]=D[i]=i;
		L[i+1]=i;R[i]=i+1;
	}R[n]=0;
	size=n+1;
	memset(H,-1,sizeof(H));
}
struct Point
{
	int x,y;
}a[N],b[N];

int dis(Point a,Point b)
{
	return (a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y);
}
int c[4000];
bool solve(int mid)
{
	init(n);
	ak = N + 1;
	rep1(i,m) rep1(j,n)
	{
		if(dis(b[i],a[j]) <= mid) Link(i,j);
	}
	if(Dance(0))
		return true;
	return false;
}
int main() {
#ifndef ONLINE_JUDGE  
	freopen("in.txt","r",stdin);  
	//   freopen("out.txt","w",stdout);  
#endif 
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m>>K;
		rep1(i,n) scanf("%d%d",&a[i].x,&a[i].y);
		rep1(i,m) scanf("%d%d",&b[i].x,&b[i].y);
		int cnt = 0;
		rep1(i,m) rep1(j,n)
		{
			c[cnt++] = dis(b[i],a[j]);
		}
		sort(c,c+cnt);
		cnt = unique(c,c+cnt) - c;
		int l = 0, r = cnt - 1;
		while(l < r)
		{
			int mid = (l + r) / 2;
			if(solve(c[mid])) r = mid;
			else l = mid + 1;
		}
		printf("%.6lf\n",sqrt(c[l] * 1.0));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值