[洛谷P3288][SCOI2014][BZOJ3597]方伯伯运椰子(网络流+图论)

Address

Solution

  • \(c_i\) 看作边 \((u_i,v_i)\) 的流量,并将每条边的容量都看作 \(∞\)
  • \(a_i\) 看作把边 \((u_i,v_i)\) 增广 \(1\) 单位流量的花费,\(b_i\) 看作将 \((u_i,v_i)\) 退 \(1\) 单位流量的花费,也就是把边 \((v_i,u_i)\) 增广 \(1\) 单位流量的花费。
  • \(d_i\) 看作把边 \((u_i,v_i)\) 增广 \(1\) 单位流量的代价,同时也看作把边 \((v_i,u_i)\) 增广 \(1\) 单位流量的收益
  • \(res=X-Y\),显然 \(\lfloor\)\((u_i,v_i)\) 增广 \(1\) 单位流量 \(\rfloor\)\(res\) 的贡献是 \(-b_i-d_i\)\(\lfloor\)\((v_i,u_i)\) 增广 \(1\) 单位流量 \(\rfloor\)\(res\) 的贡献是 \(d_i-a_i\)
  • 注意 \(c_i=0\) 时,不能增广 \((v_i,u_i)\)
  • 先按上面所说的建好一张新图。
  • 题目要求流量不能变少,又因为流量变多肯定不优,所以我们要让流量不变。
  • 也就是说我们要增广一些环。
  • 看到 \((X-Y)/k\),显然的分数规划模型。
  • 二分答案,判断是否 \(res/k≥mid\),即是否 \(res-mid*k≥0\)
  • 考虑将新图上的每条边减去 \(mid\),然后如果图上存在一个环使得边权和非负,那么 \(res/k≥mid\)

Code

#include <bits/stdc++.h>

using namespace std;

template <class t>
inline void read(t & res)
{
   char ch;
   while (ch = getchar(), !isdigit(ch));
   res = ch ^ 48;
   while (ch = getchar(), isdigit(ch))
   res = res * 10 + (ch ^ 48);
}

const int e = 1e5 + 5;
const double inf = 1e18, eps = 1e-4;
int n, m, adj[e], nxt[e], go[e], num, s, cnt[e];
double len[e], ans, dis[e];
bool vis[e];

inline void add(int x, int y, double v)
{
   nxt[++num] = adj[x]; adj[x] = num; go[num] = y; len[num] = v;
}

inline bool check(double mid)
{
   queue<int>q;
   int i;
   for (i = 1; i <= n + 2; i++) vis[i] = 1, dis[i] = 0, cnt[i] = 1, q.push(i);
   while (!q.empty())
   {
   	int u = q.front();
   	q.pop();
   	vis[u] = 0;
   	for (i = adj[u]; i; i = nxt[i])
   	{
   		int v = go[i];
   		if (dis[u] + len[i] - mid >= dis[v])
   		{
   			dis[v] = dis[u] + len[i] - mid;
   			cnt[v] = cnt[u] + 1;
   			if (cnt[v] >= n + 2) return 1;
   			if (!vis[v]) q.push(v), vis[v] = 1;
   		}
   	}
   }
   return 0;
}

int main()
{
   int i, u, v, ai, bi, ci, di;
   read(n); read(m); m--;
   while (m--)
   {
   	read(u); read(v); read(ai); read(bi); read(ci); read(di);
   	add(u, v, -bi - di);
   	if (ci) add(v, u, di - ai);
   }
   read(u); read(s); read(ai); read(bi); read(ci); read(di);
   double l = 0, r = 1e9;
   while (r - l > eps)
   {
   	double mid = (l + r) / 2.0;
   	if (check(mid)) ans = l = mid;
   	else r = mid;
   }
   printf("%.2lf\n", ans);
   return 0;
}
原文地址:https://www.cnblogs.com/cyf32768/p/12196051.html