洛谷2494 [SDOI2011]保密 (分数规划+最小割)

自闭一早上

分数规划竟然还能被卡精度

首先假设我们已经知道了到每个出入口的时间(代价)

那我们应该怎么算最小的和呢?

一个比较巧妙的想法是,由于题目规定的是二分图。

我们不妨通过最小割的形式。
表示这个基地必须从两个口之一进,从(S)连到奇数点,偶数点连到(T),流量是到这个点的时间。
然后对于每个空腔的(u和v,(u->v,inf))表示这个二者至少要到一个。
那么这样跑一遍最小割,就表示经过所有空腔的最小代价。
那么现在其实问题就转化成了
如果求到一个点的时间(a[i])

观察题目中的柿子。
(a[i]=frac{sum t}{sum s})
哎!这个是不是可以直接分数规划啊。

因为有无限的军队,所以每个点之间是可以单独处理的。

首先我们先考虑对于任意一个点(x)

不妨直接二分
(mid>frac{sum t}{sum s})

[sum t-sum s imes mid<0 ]

如果我们将边权设为(t-mid*s)
那么我们只需要判断到一个点的最短路是否是小于0,如果小于,那么(ans<mid),继续二分即可。

那么我们对于每一个点都做一个这样的过程,(a[i])也就能求出来了啦

qwq
这有一个需要注意的地方,也是我做的时候看题解才用的一个剪枝。

就是我们用(spfa)求最短路的时候,只要遇到了目标点,并且(dis<=0),直接(return)

这样能大大加快你程序的速度。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define db double
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 1410;
const int maxm = 1e6+1e2;
const double inf = 1e12;
const double eps = 1e-4 ;
int point[maxn],nxt[maxm],to[maxm];
db val[maxm];
int cnt=1,n,m;
int h[maxn];
int x[maxm],y[maxm];
double w[maxm],p[maxm];
int s,t;
int st;
double dis[maxn],cost[maxm];
int vis[maxn];
void init()
{
  cnt=1;
  memset(point,0,sizeof(point));
} 
void addedge(int x,int y,db w)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    point[x]=cnt;
    val[cnt]=w;
}
void add(int x,int y,double w)
{
    //printf("%d %d %.2lf
",x,y,w);
    nxt[++cnt]=point[x];
    to[cnt]=y;
    cost[cnt]=w;
    point[x]=cnt;
}
void insert(int x,int y,db w)
{
    //cout<<x<<" "<<y<<" "<<w<<endl; 
    addedge(x,y,w);
    addedge(y,x,0);
}
queue<int> q;
void spfa(int s,int ed)
{
    for (int i=1;i<=maxn-2;i++) dis[i]=inf;
    memset(vis,0,sizeof(vis));
    while (!q.empty()) q.pop();
    dis[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        vis[x]=0;
        for (int i=point[x];i;i=nxt[i])
        {
            int p = to[i];
            if (dis[p]>dis[x]+cost[i])
            {
                dis[p]=dis[x]+cost[i];
                if (p==ed && dis[p]<=-eps) return;
                if (!vis[p])
                {
                    q.push(p);
                    vis[p]=1;
                }
            }
        } 
    }
}
bool bfs(int s)
{
    memset(h,-1,sizeof(h));
    while (!q.empty()) q.pop();
    h[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i=point[x];i;i=nxt[i])
        {
            int p = to[i];
            if (h[p]==-1 && val[i]>0) 
            {
                h[p]=h[x]+1;
                q.push(p);
            }
        }
    }
    if (h[t]==-1) return false;
    return true;
}
db dfs(int x,db low)
{
   if (x==t ||low==0) return low;
   db totflow=0;
   for (int i=point[x];i;i=nxt[i])
   {
   	  int p = to[i];
   	  if (val[i]>0 && h[p]==h[x]+1)
   	  {
   	  	 db tmp = dfs(p,min(low,val[i]));
   	  	 val[i]-=tmp;
   	  	 val[i^1]+=tmp;
   	  	 low-=tmp;
   	  	 totflow+=tmp;
   	  	 if (low==0) return totflow;
      }
   }
   if (low>0) h[x]=-1;
   return totflow;
}
db dinic()
{
    db ans=0;
    while (bfs(s))
    {
        ans=ans+dfs(s,inf);
    }
    return ans;
}

double uu=0;
bool check(double mid,int xx)
{
    init();
    for (int i=1;i<=m;i++)
      add(x[i],y[i],w[i]-mid*p[i]);
    spfa(n,xx);
    return (dis[xx]<=-eps);
}
double solve(int x)
{
    double l=0,r=2e9;
    double ans=inf;
    while (r-l>=1e-4)
    {
        double mid = (l+r)/2;
        if(check(mid,x)) r=mid,ans=mid;
        else l=mid;
    }
    return ans;
}
double a[maxn];
signed main()
{
  n=read(),m=read();
  for (int i=1;i<=m;i++) 
    scanf("%lld%lld%lf%lf",&x[i],&y[i],&w[i],&p[i]);
  int num1=read(),num2=read();
  init();
  s=maxn-10;
  t=s+1;
  for (int i=1;i<=num2;i++)
  {
  	 double pp = solve(i);
  	 a[i]=pp;
  }
  init();
  for (int i=1;i<=num2;i++)
  {
  	 if (i&1) insert(s,i,a[i]);
  	 else insert(i,t,a[i]);
  }
  for (int i=1;i<=num1;i++)
  {
  	 int u=read(),v=read();
  	 if (v&1) swap(u,v);
  	 if (a[u]==inf && a[v]==inf) 
  	 {
  	 	cout<<-1;
  	 	return 0;
     }
  	 insert(u,v,inf);
  }
  double ptx = dinic();
  printf("%.1lf
",ptx);
  return 0;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10186740.html