HDU - 3126 Nova(二分+最大流+計算幾何)

鏈接:https://cn.vjudge.net/problem/HDU-3126

題意:多組樣例,n個巫師,m個敵人,k顆樹。巫師有攻擊距離和冷卻時間,樹有半徑。若敵人在巫師的攻擊範圍外,或者巫師和敵人之間被樹擋着,都不可攻擊。問巫師消滅所有敵人的最少時間。

思路:首先,巫師消滅敵人的個數和時間是成正比的,也就是說是單調的。這滿足二分的要求,考慮二分枚舉時間T,那麼巫師(i)可以消滅敵人的個數爲\left \lfloor \frac{T}{lich[i].t]} \right \rfloor+1,(加一是因爲消滅第一個人不需要冷卻時間,並且數據中應該沒有t爲0的情況。)每次跑一遍最大流檢驗是否可以全部消滅敵人即可。至於實現,預處理出每個巫師可以消滅的敵人。

1、巫師(i)與源點建邊,容量爲\left \lfloor \frac{T}{lich[i].t]} \right \rfloor+1

2、巫師(u)與可消滅的敵人建邊(v),容量爲1。

3、敵人(i)與匯點(t)連邊,容量爲1。

 

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 610;
const int M = 1e5+10;
const double eps = 1e-7;
const int inf =0x3f3f3f3f;
int sgn(double x)
{
	if(fabs(x)<eps) return 0;
	else if(x<0) return -1;
	else return 1;
}
struct Point
{
	double x,y;
	Point(){}
	Point(double x,double y):x(x),y(y){}
	Point operator -(const Point& b)const//相減 
	{
		return Point(x-b.x,y-b.y);
	}
	double operator ^(const Point& b)const//叉乘 
	{
		return x*b.y-y*b.x;
	}
	double operator *(const Point& b)const//點乘 
	{
		return x*b.x+y*b.y;
	}		
};
struct Line{ Point s,e; Line(){} Line(Point _s,Point _e){ s = _s; e = _e; } };
struct character { double x,y,r; int t; }li[210],wi[210],tr[210];
double dis(Point a,Point b){ return sqrt((a-b)*(a-b)); }

Point NearestPointToLineSeg(Point P,Line L)
{
    Point result;
    double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    if(t >= 0 && t <= 1){ result.x = L.s.x + (L.e.x - L.s.x)*t; result.y = L.s.y + (L.e.y - L.s.y)*t; }
    else{ if(dis(P,L.s) < dis(P,L.e)) result = L.s; else result = L.e; }
    return result;
}
//點p0到線段p1p2的距離
double pointtoseg(Point p0,Point p1,Point p2){ return dis(p0,NearestPointToLineSeg(p0,Line(p1,p2))); }
vector <int> ve[210];
int in[210];
struct node
{
	int to,ca,nxt;
}g[M];
int head[N],cnt,s,t; 
int deep[N],cur[N];
int n,m,k;
void getve()
{
	for(int i=1;i<=n;i++) ve[i].clear();
	for(int i=1;i<=m;i++) in[i]=0;
	bool no;
	double temp;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(sgn(dis(Point(wi[j].x,wi[j].y),Point(li[i].x,li[i].y))-li[i].r)>0) continue;	
			no=0;
			for(int ii=1;ii<=k;ii++)
				if(pointtoseg(Point(tr[ii].x,tr[ii].y),Point(li[i].x,li[i].y),Point(wi[j].x,wi[j].y))<=tr[ii].r)
				{
					no=1;	break;	
				}
			if(!no) in[j]++,ve[i].push_back(j);
		}
}
void Init()
{
	cnt=0;
	s=0; t=n+m+1;
	for(int i=s;i<=t;i++)
		head[i]=-1;
}
void add(int u,int v,int ca)
{
	g[cnt]=node{v,ca,head[u]},head[u]=cnt++;
}
bool bfs()
{
	queue<int> q;
	for(int i=s;i<=t;i++)
		deep[i]=0;
	int u,v;
	deep[s]=1;
	q.push(s);
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		if(u==t) return 1;
		for(int i=head[u];~i;i=g[i].nxt)
		{
			v=g[i].to;
			if(!deep[v]&&g[i].ca>0)
			{
				deep[v]=deep[u]+1;
				q.push(v);	
			}	
		}	
	}	
	return deep[t]!=0;
}
int dfs(int u,int flow)
{
	if(u==t||!flow) return flow;
	int ans=0,nowflow,v;
	for(int& i=cur[u];~i;i=g[i].nxt)
	{
		v=g[i].to;
		if(deep[v]==deep[u]+1&&g[i].ca>0)
		{
			nowflow=dfs(v,min(flow,g[i].ca));
			if(nowflow)
			{
				ans+=nowflow;
				flow-=nowflow;
				g[i].ca-=nowflow;
				g[i^1].ca+=nowflow;
				if(!flow) break;
			}
		}
	}
	if(!ans) deep[u]=0;
	return ans;
}
void buildg(int x)
{
	Init(); 
	for(int i=1;i<=n;i++)
		add(s,i,li[i].t?x/li[i].t+1:m),add(i,s,0);
	for(int i=1;i<=n;i++)
		for(int j=0;j<ve[i].size();j++)
			add(i,n+ve[i][j],1),add(n+ve[i][j],i,0);
	for(int i=1;i<=m;i++)
		add(i+n,t,1),add(t,i+n,0);	
}
bool dinic()
{	
	int maxflow=0,flow;
	while(bfs())
	{
		for(int i=s;i<=t;i++)
			cur[i]=head[i];
		while(flow=dfs(s,inf))
			maxflow+=flow;
	}		
	return maxflow==m;	
}
int main(void)
{
	int t,l,r,mid,ans,maxt;
	scanf("%d",&t);
	while(t--)
	{
		maxt=0;
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=n;i++)
			scanf("%lf%lf%lf%d",&li[i].x,&li[i].y,&li[i].r,&li[i].t),maxt=max(t,li[i].t);
		for(int i=1;i<=m;i++)
			scanf("%lf%lf",&wi[i].x,&wi[i].y);
		for(int i=1;i<=k;i++)
			scanf("%lf%lf%lf",&tr[i].x,&tr[i].y,&tr[i].r);
		getve();
		bool no=0;
		for(int i=1;i<=m;i++) if(!in[i]){ no=1; break; }	
		if(no){ puts("-1"); continue; }
		l=0,r=2*m*maxt,ans=-1;
		while(l<=r)
		{			
			mid=(l+r)>>1;
			buildg(mid);
			if(dinic())
			{
				r=mid-1;
				ans=mid;
			}
			else l=mid+1;			
		}		
		printf("%d\n",ans);
	}	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章