GYM - 101147 C.The Wall
题意:
长和宽分别为M+N/2,N的矩形中。有很多敌人的点。有两种方法消灭敌人。
1.N个桶,第i个桶可以消灭i-1<=x<i中的敌人。2.M个摆(半圆)每个摆可以消灭距离他前面不超过1以内的敌人。第i个摆的圆心在(N/2,i-1),半径都为N/2。
问消灭所有敌人消耗的最少设备是多少。
题解:
根据题意可以发现,每一个敌人都可以被桶消灭,但不一定被摆消灭。摆的盲区在第1个摆的内部和第M个摆两边的死角位置。
所以出现在盲区的点必须被桶消灭。即那些桶是必选的。
其余的点把消灭他的桶号和消灭他的摆号连边,跑一个最小顶点覆盖即可。
#include <bits/stdc++.h> using namespace std; int t; int p; double n, m; int ans; int vp[10005]; int vis[205]; int match[205]; struct ndoe { double x, y; }w[10005]; double distance(double x1, double y1, double x2, double y2) { return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } vector<int> g[205]; bool dfs(int u) { for(int i = 0; i < g[u].size(); i++) { int v = g[u][i], w = match[v]; if(vis[v]) continue; vis[v] = 1; if(w<0 || dfs(w)) { match[v] = u; return true; } } return false; } int hungarian() { int res = 0; memset(match, -1, sizeof(match)); for(int u = 0; u < n; u++) { memset(vis, 0, sizeof(vis)); if(dfs(u)) res++; } return res; } int main() { freopen("wall.in","r",stdin); scanf("%d", &t); while(t--) { ans = 0; memset(vp, 0, sizeof(vp)); scanf("%lf%lf%d", &n, &m, &p); for(int i = 0; i < n+m; i++) g[i].clear(); for(int i = 1; i <= p; i++) scanf("%lf%lf", &w[i].x, &w[i].y); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= p; i++) { if(distance(n/2, 0, w[i].x, w[i].y)<=n/2 || (w[i].y>m && distance(n/2, m-1, w[i].x, w[i].y)>1.0+n/2)) { vp[i] = 1; int v = (int)floor(w[i].x); if(vis[v]) continue; else { vis[v] = 1; ans++; } } } for(int i = 1; i <= p; i++) { if(vp[i]) continue; int judge = (int)floor(w[i].x); if(vis[judge]) continue; int up = (int)floor(w[i].y); for(int v = up; v >= 0; v--) { if(distance(n/2, (double)v, w[i].x, w[i].y) > n/2) { int u = (int)floor(w[i].x); g[u].push_back(v+n); break; } } } ans += hungarian(); printf("%d\n", ans); } }