Vijos-1228拯救世界-星际大战 最小费用最大流

[文章目录]

Description

现在我们有N(N<=20)个导弹。需要在最短的时间内,用这N个导弹摧毁敌方N个目标(1个导弹只能摧毁1个目标)。给出每个导弹的降落位置和目标的位置,请你编程帮助给每个导弹确定目标,使每个导弹到其目标的距离之和最小

建立一张图,s向每个导弹连边,每个目标向t连边,边权都为1。费用为0,使得跑出来的最大流就是n,(即每个导弹都打了一个目标)。然后n个导弹向n个目标连边,边权一定要为1(在vijos  inf居然过了,ss啦!!!)【因为如果边权为inf时会造出一个反向弧,然后就形成了一个费用为零的环】,费用为两点间的距离。然后跑一边最小费用最大流就好了。

然后,看一下最小费用最大流算法。

不是说有一个EK吗,所以关键的最小费用就是在点的决定遍历顺序上,用spfa跑出一条最小费用路径,然后记录路径,增广。ans加上费用。(利用贪心思想可知费用一定是最小的)

另外由于后向弧的撤销作用,反向弧的费用需要为正向弧费用的相反数(跑了后向弧时减去一开始加上的费用)。

还有 double类型不能清成0x3f,并不是最大值。手动吧。

基本的最小费用都是在流量上有一个单价,所以需要乘一个流量(然而这题并不需要)。

代码细节比较多。需要理解。

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int x[50],y[50],gx[50],gy[50],n,s,t;
int head[50],path[50],to[1000],nxt[1000],f[1000],cnt=1,inf=0x3f3f3f3f;
double val[1000];
queue<int> q;
double Dis(int x,int y,int dx,int dy)
{
    return sqrt((x-dx)*(x-dx)+(y-dy)*(y-dy));
}
void add(int x,int y,int z,double w)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    f[cnt]=z;
    val[cnt]=w;
    head[x]=cnt;

    to[++cnt]=x;
    nxt[cnt]=head[y];
    f[cnt]=0;
    val[cnt]=-w;//反向弧的费用为负
    head[y]=cnt;
}
double dis[50];//原来bfs分层的依据更新成了费用最短路
bool v[50];//spfa
bool spfa()
{
    for(int i=(n<<1)+2;i;i--) dis[i]=inf,v[i]=0;
    while(!q.empty()) q.pop();
    q.push(s);dis[s]=0;v[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        v[x]=false;
        for(int y,i=head[x];i;i=nxt[i])
            if(f[i]>0&&dis[y=to[i]]>dis[x]+val[i])
            {
                dis[y]=dis[x]+val[i];
                path[y]=i^1;//path[i]记录的是到某个点的边的反向边的编号,用来从汇点找回路径
                if(!v[y]) q.push(y),v[y]=1;
            }
    }
    return dis[t]<inf;
}
double mincost()
{
    double ans=0;
    while(spfa())
    {
        int minflow=1<<30;
        for(int i=t;i!=s;i=to[path[i]]) minflow=min(minflow,f[path[i]^1]);
        for(int i=t;i!=s;i=to[path[i]])
        {
            f[path[i]]+=minflow;
            f[path[i]^1]-=minflow;
            ans+=val[path[i]^1]*minflow;//单价*流量
        }
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
    for(int i=1;i<=n;i++) scanf("%d%d",&gx[i],&gy[i]);
    s=(n<<1)+1;
    t=(n<<1)+2;
    for(int i=1;i<=n;i++)
    {
        add(s,i,1,0);
        add(i+n,t,1,0);
        for(int j=1;j<=n;j++)
            add(i,n+j,1,Dis(x[i],y[i],gx[j],gy[j]));
    }
    printf("%.3lf\n",mincost());
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注