题面
一道好题。
最小费用最大流+调整法($orz$杨颙)(原谅我想到杨颙,好像我们好久没见了)
先假设所有的路都被压缩至$w=0$,然后建两类边,用最小费用最大流扩大(类似的思想:棋盘占领)。
注意求出的东西应该是后来的费用$Y - $之前的费用$X + k times mid$,所以$check()$的时候要取反(这个地方我想了好久。。。。。)
#include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<queue> #include<cmath> #define ri register int #define N 5050 #define M 3050 #define INF 1000000007 #define LL long long #define T (n+2) #define S (n+1)using namespace std;int n,m; int u[M],v[M],a[M],b[M],c[M],d[M];struct graph {vector<int> to,w,ed[N];vector<double> c;double dis[N]; int cur[N]; bool vis[N];void clear() {c.clear(); to.clear(); w.clear();for (ri i=1;i<=n+2;i++) ed[i].clear();}void add_edge(int a,int b,int aw,double ac) {to.push_back(b); w.push_back(aw); c.push_back(ac); ed[a].push_back(to.size()-1);to.push_back(a); w.push_back(0); c.push_back(-ac); ed[b].push_back(to.size()-1);}bool spfa() {for (ri i=1;i<=n+2;i++) dis[i]=100000000000000.7;memset(vis,0,sizeof(vis));queue<int> q;dis[S]=0;q.push(S);vis[S]=1;while (!q.empty()) {int x=q.front(); q.pop();for (ri i=0;i<ed[x].size();i++) {int e=ed[x][i];if (dis[to[e]]>dis[x]+c[e] && w[e]) {dis[to[e]]=dis[x]+c[e];if (!vis[to[e]]) vis[to[e]]=1,q.push(to[e]);}}vis[x]=0;}return dis[T]<100000000000000.7;}int dfs(int x,int lim) {if (x==T || !lim) return lim;LL sum=0; vis[x]=1;for (ri &i=cur[x];i<ed[x].size();i++) {int e=ed[x][i];if (fabs(dis[x]+c[e]-dis[to[e]])<1e-5 && w[e] && !vis[to[e]]) {int f=dfs(to[e],min(lim,w[e]));w[e]-=f; w[1^e]+=f;lim-=f; sum+=f;if (!lim) return sum;}}return sum;}double zkw() {double ret=0;while (spfa()) {memset(vis,0,sizeof(vis));memset(cur,0,sizeof(cur));ret+=dfs(S,INF)*dis[T];}return ret;} } G;bool can(double mid) {double dec=0;for (ri i=1;i<=m;i++) dec+=(a[i]-d[i]+mid)*c[i];for (ri i=1;i<=m;i++) {G.add_edge(u[i],v[i],c[i],-(a[i]+mid-d[i]));if (u[i]!=S) G.add_edge(u[i],v[i],INF,b[i]+mid+d[i]);}if (0-(dec+G.zkw())>0) return 1; else return 0; }int main() {scanf("%d %d",&n,&m);for (ri i=1;i<=m;i++) {scanf("%d %d %d %d %d %d",&u[i],&v[i],&a[i],&b[i],&c[i],&d[i]);}double lb=0,rb=50000.0;double ans=0;while (rb-lb>1e-3) {double mid=(lb+rb)/2;//printf("%.5lf %.5lf %.5lfn",lb,rb,mid); G.clear();if (can(mid)) lb=ans=mid; else rb=mid;}printf("%.2lfn",ans);return 0; }
转载于:.html
本文发布于:2024-01-31 13:06:44,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170667760328741.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |