最开始我是想的,把每个点拆成m个点,然后用一次费用流搞,对于那些可以重复利用的工人,添加一些cost为-1的弧。很逼真把,Why wa。。。
其实每个兵种是相互独立的,那么就可以对于每个兵种,分别搞一次费用流,最后累加ans。这样代码就好撸很多了呢。。。
费用流建图:显然每个点要拆成两个点,一个入(从s向其连<INF, 1>的弧,简称x点),一个出(从该点向t连<need, 0>的弧,简称y点)。那么对于i,j两个点,如果end[i] + dist(i, j) <= start[j]的时候怎么办?如果直接从i的y点向j的j的x点连弧会使i本身的y点->t的弧无法满流,显然会影响最优解。所以每个点其实呀拆成3个点,新加的z点,代表去某点的工人重复利用的流量。首先要从s向每个z点连<need, 0>的弧,那么对于重复利用的情况,从i的z点向j的y点连<need, 0>的弧就行啦!
ps:用ZKW费用流快多了。。。78ms。。。。
#include<functional> #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<vector> #include<queue> #define REP(i, n) for(int i=0; i<n; i++) #define FF(i, a, b) for(int i=a; i<b; i++) #define CLR(a, b) memset(a, b, sizeof(a)) #define PB push_back using namespace std; const int MAXN = 455; const int MAXE = MAXN*MAXN/2; const int INF = 1e9; struct ZKW_flow{ int st, ed, ecnt, n; int head[MAXN]; int cap[MAXE], cost[MAXE], to[MAXE], next[MAXE], dis[MAXN]; ; void init(){ memset(head, 0, sizeof(head)); ecnt = 2; } void AddEdge(int u, int v, int cc, int ww){ cap[ecnt] = cc; cost[ecnt] = ww; to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++; cap[ecnt] = 0; cost[ecnt] = -ww; to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++; } void SPFA(){ REP(i, n+1) dis[i] = INF; priority_queue<pair<int, int> > Q; dis[st] = 0; Q.push(make_pair(0, st)); while(!Q.empty()){ int u = Q.top().second, d = -Q.top().first; Q.pop(); if(dis[u] != d) continue; for(int p = head[u]; p; p = next[p]){ int &v = to[p]; if(cap[p] && dis[v] > d + cost[p]){ dis[v] = d + cost[p]; Q.push(make_pair(-dis[v], v)); } } } REP(i, n+1) dis[i] = dis[ed] - dis[i]; } int minCost, maxFlow; bool use[MAXN]; int add_flow(int u, int flow){ if(u == ed){ maxFlow += flow; minCost += dis[st] * flow; return flow; } use[u] = true; int now = flow; for(int p = head[u]; p; p = next[p]){ int &v = to[p]; if(cap[p] && !use[v] && dis[u] == dis[v] + cost[p]){ int tmp = add_flow(v, min(now, cap[p])); cap[p] -= tmp; cap[p^1] += tmp; now -= tmp; if(!now) break; } } return flow - now; } bool modify_label(){ int d = INF; REP(u, n+1) if(use[u]) for(int p = head[u]; p; p = next[p]){ int &v = to[p]; if(cap[p] && !use[v]) d = min(d, dis[v] + cost[p] - dis[u]); } if(d == INF) return false; REP(i, n+1) if(use[i]) dis[i] += d; return true; } int Mincost(int ss, int tt, int nn){ st = ss, ed = tt, n = nn; minCost = maxFlow = 0; SPFA(); while(true){ while(true){ CLR(use, 0); if(!add_flow(st, INF)) break; } if(!modify_label()) break; } return minCost; } }solver; int T, n, m, s, t; struct Point { double x, y, st, ed; int need[11]; void get() { scanf("%lf%lf%lf%lf", &x, &y, &st, &ed); ed += st; REP(i, m) scanf("%d", &need[i]); } double dist(Point rhs) { return sqrt((x-rhs.x)*(x-rhs.x) + (y-rhs.y)*(y-rhs.y)); } }pt[155]; int gao() { int ret = 0; REP(j, m) { solver.init(); FF(i, 1, n) { solver.AddEdge(s, i, INF, 1); solver.AddEdge(i, i + 2*n, pt[i].need[j], 0); solver.AddEdge(i + 2*n, t, pt[i].need[j], 0); solver.AddEdge(s, i+n, pt[i].need[j], 0); } FF(i, 1, n) FF(k, 1, n) if(i != k) { if(pt[i].ed + pt[i].dist(pt[k]) > pt[k].st) continue; solver.AddEdge(i + n, k + 2*n, pt[i].need[j], 0); } ret += solver.Mincost(s, t, t); } return ret; } int main() { scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); s = 0; t = n*3 + 1; scanf("%lf%lf", &pt[0].x, &pt[0].y); FF(i, 1, n) pt[i].get(); printf("%d\n", gao()); } return 0; }