[JZOJ2393]【ZJOI2011】营救皮卡丘

Description

皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。
火箭队一共有N个据点,据点之间存在M条双向道路。据点分别从1到N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点。
由于火箭队的重重布防,要想摧毁K号据点,必须按照顺序先摧毁1到K-1号据点,并且,如果K-1号据点没有被摧毁,由于防御的连锁性,小智一行任何一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,任何人是不能够经过K号据点的。
为了简化问题,我们忽略战斗环节,小智一行任何一个人经过K号据点即认为K号据点被摧毁。被摧毁的据点依然是可以被经过的。
K个人是可以分头行动的,只要有任何一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,只要N号据点被摧毁,皮卡丘就得救了。
野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同时,使得K个人所经过的道路的长度总和最少。
请你帮助小智设计一个最佳的营救方案吧!
对于100%的数据满足N ≤ 150, M ≤ 20 000, 1 ≤ K ≤ 10, Li ≤ 10 000, 保证小智一行一定能够救出皮卡丘。
至于为什么K ≤ 10,你可以认为最终在小智的号召下,小智,小霞,小刚,小建,小遥,小胜,小光,艾莉丝,天桐,还有去日本旅游的黑猫警长,一同前去大战火箭队。

Solution

经典的最小权路径可相交覆盖问题。
但原图不是一个DAG

可以先跑一遍floyd,求出两个点之间合法最短路径,即经过的点都小于等于两端。

显然如果中转点k比i,j都要大,那么它是不能作为中转的

考虑建图。

每个点拆成入点和出点

S向0号点入点连容量为K,费用为0的边,向1~N入点连容量为1,费用为0的
对于一对点(x,y), x<y ,X的入点向Y的出点连费用为dis[x][y],容量为1的

1~N的向T连费用为0,容量为1的点

感受一下它的正确性。

直接跑费用流即可。

Code

#include 
#include 
#include 
#include 
#include 
#include 
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 305
#define LL long long
using namespace std;
int n,m,l,ds[N][N],c[N][N],f[N][N],d[N],a[N][N],n1,ans,st,ed,in[N],out[N];
bool bz[N];
void link(int x,int y,int v,int s)
{
    c[x][y]=s,f[x][y]=v,c[y][x]=-s;
    a[x][++a[x][0]]=y;
    a[y][++a[y][0]]=x;
}
void floyd()
{
    fo(k,1,n)
    {
        fo(i,1,n)
        {
            fo(j,1,n)
            {
                if(k<=i||k<=j)
                {
                    if(i==j) ds[i][j]=0;
                    else ds[i][j]=min((LL)ds[i][j],(LL)ds[i][k]+(LL)ds[k][j]);
                }
            }
        }
    }
}
int zkw(int k,int l)
{
    if(k==ed) 
    {
        ans+=l*d[st];
        return l;
    }
    int s=0;
    bz[k]=1;
    fo(i,1,a[k][0])
    {
        int p=a[k][i];
        if(f[k][p]&&d[p]+c[k][p]==d[k]&&!bz[p]) 
        {
            int t=zkw(p,min(l,f[k][p]));
            s+=t,l-=t,f[k][p]-=t,f[p][k]+=t;
            if(!l) return s;
        }
    }
    return s;
}
bool fd()
{
    int mi=1802201963;
    fo(k,1,n1)
    {
        if(bz[k])
        {
            fo(i,1,a[k][0])
            {
                int p=a[k][i];
                if(f[k][p]&&!bz[p]) mi=min(mi,d[p]+c[k][p]-d[k]);
            }
        }
    }
    if (mi==1802201963||mi<-1) return 0;
    fo(k,1,n1) if(bz[k]) d[k]+=mi;
    return 1;
}
int main()
{
    cin>>n>>m>>l;
    memset(ds,107,sizeof(ds));
    fo(i,1,m)
    {   
        int x,y,z;
        scanf("%d%d",&x,&y);
        x++,y++;
        scanf("%d",&z);
        ds[x][y]=min(ds[x][y],z);
        ds[y][x]=ds[x][y];
    }
    n++;
    floyd();
    n1=2;
    fo(i,1,n) in[i]=++n1,out[i]=++n1;
    fo(i,1,n)
    {
        fo(j,i+1,n)
        {
            if(ds[i][j]!=1802201963) link(in[i],out[j],1,ds[i][j]);
        }
    }
    st=1,ed=2;
    link(st,in[1],l,0);
    fo(i,2,n) link(st,in[i],1,0);
    fo(i,2,n) link(out[i],ed,1,0);
    ans=0;
    do
        do 
            memset(bz,0,sizeof(bz));
        while(zkw(st,1802201963));
    while(fd());
    printf("%d\n",ans);
}

你可能感兴趣的:(题解,————费用流)