HDU 4606 Occupy Cities 解题报告

编程入门 行业动态 更新时间:2024-10-10 00:22:16

HDU 4606 Occupy Cities 解题<a href=https://www.elefans.com/category/jswz/34/1770268.html style=报告"/>

HDU 4606 Occupy Cities 解题报告

题目

2013 多校训练 第一场

题意:

有n个城市,m个边界线,p名士兵。现在士兵要按一定顺序攻占城市,但从一个城市到另一个城市的过程中不能穿过边界线。士兵有一个容量为K的背包装粮食,士兵到达一个城市可以选择攻占城市或者只是路过,如果攻占城市,就能装满背包。从城市到城市消耗的粮食等于两城市的距离,如果距离大于士兵当前的背包的容量,士兵就不能走这条路。士兵可以选择空降一次,空降不耗费。求p个士兵攻占完所有城市所需要的最小背包容量k。

解法:

很容易想到二分答案。

先用线段求交和floyed求出两个城市之间的最短距离(即两城市路上不攻占任何城市的最小花费),两个城市(u,v)如果从u到v需要跨过边境线或者城市u在v之后被攻占其距离就设为inf。

现在问题变成p个士兵攻打n个城市,如果某两个城市间距离小于mid就可达,如何判断能否攻占完n个城市?

利用二分图匹配,最小路径覆盖:在一个P*P的有向图中,在图中找一些路经,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联,最小路径=点数-最大匹配数。只需要判断最小路径覆盖是否小于等于p即可,因为最小路径覆盖即攻占完所有城市所需要的最少士兵数。

代码:

#include #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 105
const double EPS=1e-6;
const int INF=1000000000;
int n,m,p;
int ord[maxn],result[maxn];
bool state[maxn];
double dis[maxn*3][maxn*3];
int dcmp(double x)
{if (fabs(x)<EPS) return 0;return x<0?-1:1;
}
struct Point
{double x,y;Point(){}Point(double a,double b):x(a),y(b){}Point operator+(const Point &a)const{ return Point(x+a.x,y+a.y); }Point operator-(const Point &a)const{ return Point(x-a.x,y-a.y); }void input(){scanf("%lf%lf",&x,&y);}
}C[maxn*3];
struct Line
{Point a,b;
}L[maxn];
typedef Point Vector;
double Cross(Vector a,Vector b)
{return a.x*b.y-a.y*b.x;
}
double Dot(Vector a,Vector b)
{return a.x*b.x+a.y*b.y;
}
double Dist(Vector a)
{return sqrt(Dot(a,a));
}
bool Ispinter(Point a,Point b,Point c,Point d)
{double c1=Cross(b-a,c-a),c2=Cross(b-a,d-a),c3=Cross(d-c,a-c),c4=Cross(d-c,b-c);return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
int find(int x,double key)
{for (int i=1;i<=n;i++)if (!state[i]&&dis[x][i]<key+EPS){state[i]=1;if (!result[i]||find(result[i],key)){result[i]=x;return 1;}}return 0;
}
int solve(double key)
{int ans=0;memset(result,0,sizeof(result));for (int i=1;i<=n;i++){memset(state,0,sizeof(state));if (find(i,key)) ans++;}return n-ans;
}
int main()
{//freopen("/home/christinass/code/in.txt","r",stdin);int cas,d,num;scanf("%d",&cas);while (cas--){scanf("%d%d%d",&n,&m,&p);for (int i=1;i<=n;i++) C[i].input();num=n;for (int i=1;i<=m;i++){L[i].a.input(),L[i].b.input();C[++num].x=L[i].a.x,C[num].y=L[i].a.y;C[++num].x=L[i].b.x,C[num].y=L[i].b.y;}for (int i=1;i<=n;i++){scanf("%d",&d);ord[d]=i;}for (int i=1;i<=num;i++)for (int j=1;j<=num;j++) dis[i][j]=INF;for (int i=1;i<=num;i++)for (int j=i+1;j<=num;j++){bool flag=1;for (int k=1;k<=m&&flag;k++)if (Ispinter(C[i],C[j],L[k].a,L[k].b)) flag=0;if (flag) dis[i][j]=dis[j][i]=Dist(C[i]-C[j]);}for (int k=1;k<=num;k++)for (int i=1;i<=num;i++)for (int j=1;j<=num;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (ord[i]>=ord[j]) dis[i][j]=INF;double l=0,r=INF;while (l<r-EPS){double mid=(l+r)/2;if (solve(mid)<=p) r=mid;else l=mid;}printf("%.2f\n",(l+r)/2);}return 0;
}

小结:

二分图有关的其他性质:

最小点覆盖=最大匹配。给定一个二分图G=(V,E),定义一个点如果被覆盖,那么称所有与这个点相邻的弧被覆盖,求最少需要覆盖多少个点才能覆盖所有的边。

最大独立集=顶点数-最大匹配。独立集:图中两两互不连通的顶点构成的集合。

最小路径覆盖=顶点数-最大匹配。

更多推荐

HDU 4606 Occupy Cities 解题报告

本文发布于:2024-02-14 15:45:27,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1763921.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:报告   HDU   Cities   Occupy

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!