[bzoj2324][ZJOI2011]营救皮卡丘

来自FallDream的博客,未经允许,请勿转载,谢谢。


皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。

火箭队一共有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个人所经过的道路的长度总和最少。

请你帮助小智设计一个最佳的营救方案吧! n<=150 k<=10

 

首先这个数据范围显然是费用流qaq

然后自己yy了一个建图,把原图拆点,强制他们之间的边流1,然后从S向0连k的边,向每个点的出点连1的边,直接把原图的边扔进去了,轻松wa题,想了想貌似没法保证它们走的顺序....

所以考虑把两点之间可行的最短路径直接求出来,然后建边,就能保证结果合法了。也就是对于i->j的路径,强制只走小等于j的点,floyd求出之后从i的出点向j的入点连费用是dis[i][j]的边就行了。

嗯这个强制流1其实就是从一个点的入点向T连流量为1的边,从S向出点连流量为1的边,满足最大流的时候一定满流。如果还不是很清楚可以学习一下带上下界网络流的一套理论。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define S 0
#define T 303
#define INF 1000000000
using namespace std;
inline ll read()
{
    ll x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,m,k,cnt=1,head[T+5],ans=0,pi=0,d[T+5],dis[155][155];
bool mark[T+5],inq[T+5];
deque<int> q;
struct edge{int to,next,w,c;}e[80005];

inline void ins(int f,int t,int w,int c)
{
    e[++cnt]=(edge){t,head[f],w,c}; head[f]=cnt;
    e[++cnt]=(edge){f,head[t],0,-c};head[t]=cnt;
} 

bool modlabel()
{
    q.push_back(T);
    for(int i=S;i<T;i++)d[i]=INF;d[T]=0;inq[T]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop_front();
        for(int i=head[x];i;i=e[i].next)
            if(e[i^1].w&&d[x]+e[i^1].c<d[e[i].to])
            {
                d[e[i].to]=d[x]+e[i^1].c;
                if(!inq[e[i].to])
                {
                    inq[e[i].to]=1;
                    if(d[e[i].to]<d[q.size()?q.front():0]) q.push_front(e[i].to);
                    else q.push_back(e[i].to);
                }
            }
        inq[x]=0;
    }
    for(int i=S;i<=T;i++)    
        for(int j=head[i];j;j=e[j].next)
            e[j].c+=d[e[j].to]-d[i];
    return pi+=d[S],d[S]<INF;
}

int dfs(int x,int f)
{
    if(x==T) return ans+=pi*f,f;
    int used=0;mark[x]=1;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].w&&!e[i].c&&!mark[e[i].to])
        {
            int w=dfs(e[i].to,min(f-used,e[i].w));
            used+=w;e[i].w-=w;e[i^1].w+=w;
            if(f==used) return f; 
        }
    return used;
}

int main()
{
    n=read()+1;m=read();k=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j) dis[i][j]=INF;
    ins(S,1,k,0);
    for(int i=2;i<=n;i++) ins(i+n,T,1,0),ins(S,i,1,0);
    for(int i=1;i<=m;i++)
    {
        int x=read()+1,y=read()+1,c=read();
        dis[x][y]=min(dis[x][y],c);
        dis[y][x]=min(dis[y][x],c);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dis[i][k]+dis[k][j]<dis[i][j]&&max(i,j)>=k)
                    dis[i][j]=dis[i][k]+dis[k][j];
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(dis[i][j]<INF) ins(i,j+n,INF,dis[i][j]);
    while(modlabel())
        do memset(mark,0,sizeof(mark));
        while(dfs(S,INF));
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2017-04-18 20:28  FallDream  阅读(226)  评论(0编辑  收藏  举报