HDU - 3126 Nova(最大流+二分+简单几何)
題目鏈接:點擊查看
題目大意:給出一個二維平面坐標系,其中有n個巫師,m個敵人,以及k棵樹,規定每個巫師都有一個攻擊范圍,可以攻擊以巫師為圓心,以攻擊范圍為半徑,形成的圓內的所有敵人,對于每棵樹都有一個半徑,規定以每棵樹為圓心,以半徑形成的圓都是障礙物,若巫師和敵人之間被至少一棵樹遮擋(直線),則巫師無法攻擊敵人,每個巫師的攻擊都有冷卻時間,現在問至少需要多長時間才能消滅所有敵人,若無法消滅所有敵人,輸出-1
題目分析:題意不難懂,分析起來也比較簡單,首先因為是在二維平面坐標系中處理問題,涉及到了平面幾何的點及直線的關系,我直接套用了zx學長的幾何模板,預處理出每個巫師可以攻擊到的敵人,只要滿足巫師和敵人之間沒有任何樹遮擋并且巫師和敵人的距離在巫師的攻擊范圍內就可以建立關系,在預處理時就可以順便把-1的情況判斷出來了,如果至少存在一個敵人無法和任何一個巫師匹配,那么就是-1的情況了,隨后我們就可以直接二分了,因為每個巫師的攻擊都有冷卻時間,所以時間和消滅的敵人數顯然保持著單調性,直接二分時間就好了,至于判斷,我們可以用最大流,每次判斷一下當前的時間下是否可以消滅全部敵人,因為在某個時間t下,每個巫師可以消滅的敵人是t/lich_time+1,+1是因為第一個敵人不需要冷卻時間,這樣我們如此建邊:
就是一個簡單的最大流,如此實現上述過程就是本題答案了
我是被一個小細節卡了半天,真的有點無語,就是在判斷每棵樹時,需要將其邊界也算上(廢話),就因為少了一個等號,WA了老半天了,自閉
再就是因為我碼風的原因,可能代碼看起來比較冗長,不過真的理解這個題目之后,實現起來并不是非常復雜的
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const double eps=1e-8; const int N=410;bool vis[N];//標記敵人是否匹配struct Edge {int to,w,next; }edge[N*N];//邊數int head[N],cnt,n,m,k;void addedge(int u,int v,int w) {edge[cnt].to=v;edge[cnt].w=w;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].to=u;edge[cnt].w=0;//反向邊邊權設置為0edge[cnt].next=head[v];head[v]=cnt++; }vector<int>node[N];//記錄巫師與敵人的匹配情況int sgn(double x)//浮點型數值是否為 0 {if(abs(x)<eps) return 0;if(x<0) return -1; return 1; }struct Point//二維點 {double x,y;Point(){}Point(double xx,double yy){ x=xx,y=yy;}Point operator - (const Point &b)const{ return Point(x-b.x,y-b.y); }double operator * (const Point &b)const { return x*b.x+y*b.y; }Point operator / (const double &k)const { return Point(x/k,y/k); }double operator ^ (const Point &b)const { return x*b.y-y*b.x; }//兩點距離 double dis(const Point &b)const { return hypot(x-b.x,y-b.y); } };struct Line {Point s,e;Line(){}//兩點Line(const Point ss,const Point ee){ s=ss,e=ee;}//求線段長度 double len() { return s.dis(e); }//點到直線的距離double dis_point_to_line(const Point &p) { return abs((p-s)^(e-s))/len(); }//點到線段的距離double dis_point_to_seg(const Point &p){if(sgn((p-s)*(e-s))<0||sgn((p-e)*(s-e))<0) return min(p.dis(s),p.dis(e)); return dis_point_to_line(p);} };int d[N],now[N];//深度 當前弧優化bool bfs(int s,int t)//尋找增廣路 {memset(d,0,sizeof(d));queue<int>q;q.push(s);now[s]=head[s];d[s]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;if(d[v])continue;if(!w)continue;d[v]=d[u]+1;now[v]=head[v];q.push(v);if(v==t)return true;}}return false; }int dinic(int x,int t,int flow)//更新答案 {if(x==t)return flow;int rest=flow,i;for(i=now[x];i!=-1&&rest;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;if(w&&d[v]==d[x]+1){int k=dinic(v,t,min(rest,w));if(!k)d[v]=0;edge[i].w-=k;edge[i^1].w+=k;rest-=k;}}now[x]=i;return flow-rest; }void init()//網絡流初始化 {memset(now,0,sizeof(now));memset(head,-1,sizeof(head));cnt=0; }int solve(int st,int ed)//網絡流求解 {int ans=0,flow;while(bfs(st,ed))while(flow=dinic(st,ed,inf))ans+=flow;return ans; }struct Liches {double x,y,r;int t;void input(){scanf("%lf%lf%lf%d",&x,&y,&r,&t);} }lich[N];//巫師struct Wisps {double x,y;void input(){scanf("%lf%lf",&x,&y);} }wisp[N];//敵人struct Tree {double x,y,r;void input(){scanf("%lf%lf%lf",&x,&y,&r);} }tree[N];//樹bool check(Line L)//檢查巫師和敵人之間是否被樹遮擋 {for(int i=1;i<=k;i++){Point T(tree[i].x,tree[i].y);if(L.dis_point_to_seg(T)<=tree[i].r)//這個等號讓我自閉了老半天return false;}return true; }bool Init()//初始化巫師與敵人的關系 {memset(vis,false,sizeof(vis));for(int i=1;i<=n;i++){node[i].clear();Point L(lich[i].x,lich[i].y);for(int j=1;j<=m;j++){Point W(wisp[j].x,wisp[j].y);if(L.dis(W)>lich[i].r)continue;if(check(Line(L,W))){node[i].push_back(j);vis[j]=true;}}}for(int i=1;i<=m;i++)//順便判斷-1的情況if(!vis[i])return false;return true; }bool cal(int x)//用網絡流判斷當前時間是否可行 {init();int st=N-1,ed=st-1;for(int i=1;i<=n;i++){int time=x/lich[i].t+1;addedge(st,i,time);for(int j=0;j<node[i].size();j++)addedge(i,node[i][j]+n,1);}for(int i=1;i<=m;i++)addedge(i+n,ed,1);return solve(st,ed)==m; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int w;cin>>w;while(w--){scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=n;i++)lich[i].input();for(int i=1;i<=m;i++)wisp[i].input();for(int i=1;i<=k;i++)tree[i].input();if(!Init()){puts("-1");continue;}int l=0,r=inf,ans=-1;while(l<=r)//二分時間{int mid=l+r>>1;if(cal(mid)){ans=mid;r=mid-1;}elsel=mid+1;}printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的HDU - 3126 Nova(最大流+二分+简单几何)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 3987 Harry Pot
- 下一篇: LightOJ - 1409 Rent