zoukankan      html  css  js  c++  java
  • P4542 [ZJOI2011]营救皮卡丘(Floyd+网络流)

    P4542 [ZJOI2011]营救皮卡丘

    乍一看似乎没啥题相似的
    仔细一看,$N<=150$
    边又是双向边,似乎可以用Floyd搞
     
    先跑一遍Floyd处理出$dis[i][j]$
    注意到走据点要先走小的才能走大的
    也就是说,$i<j<k$时,$dis[i][j]$不能从$k$转移过来
    并且实际走路径时,编号也必须从小到大
     
    于是题目转化成了:
    给定序列$0,1,2,3,.....,n-1,n$,给出每两个数字之间的转移代价$dis[i][j](i<j)$,
    用$k$条从0开始的子序列覆盖整条序列的最小代价,且每个数(除0外)恰好被覆盖一次。(你闲着没事走两次干啥)
    有没有可能$i$到$j$的最优路径中间经过$k$,使$k$被覆盖2次?不会,因为在Floyd中已经处理掉了。
     
    卧槽这不是P2469 [SDOI2010]星际竞速吗!
    于是就转化为一个最小路径覆盖问题写辣
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define N 505
    #define M 200005
    int n,m,k,S,T0,T,tC,dis[N][N],d[N],a[N],p[N];
    queue <int> h; bool inh[N];
    int cnt=1,hd[N],nxt[M],ed[N],poi[M],val[M],cst[M];
    inline void adde(int x,int y,int v1,int v2){
        nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
        ed[x]=cnt, poi[cnt]=y, val[cnt]=v1, cst[cnt]=v2;
    }
    inline void link(int x,int y,int v1,int v2){adde(x,y,v1,v2),adde(y,x,0,-v2);}
    bool bfs(){
        memset(d,63,sizeof(d)); int Inf=d[0];
        h.push(S); inh[S]=1; a[S]=Inf; d[S]=0;
        while(!h.empty()){
            int x=h.front(); h.pop(); inh[x]=0;
            for(int i=hd[x];i;i=nxt[i]){
                int to=poi[i];
                if(val[i]>0&&d[to]>d[x]+cst[i]){
                    d[to]=d[x]+cst[i]; p[to]=i;
                    a[to]=min(a[x],val[i]);
                    if(!inh[to]) h.push(to),inh[to]=1;
                }
            }
        }if(d[T]==Inf) return 0;
        tC+=a[T]*d[T];
        for(int i=T;i!=S;i=poi[p[i]^1])
            val[p[i]]-=a[T],val[p[i]^1]+=a[T];
        return 1;
    }
    void Floyd(){
        memset(dis,63,sizeof(dis));
        for(int i=0;i<=n;++i) dis[i][i]=0;
        for(int i=1,u,v,w;i<=m;++i){
            scanf("%d%d%d",&u,&v,&w);
            dis[u][v]=dis[v][u]=min(dis[v][u],w);
        }
        for(int k=0;k<=n;++k)
            for(int i=0;i<=n;++i)
                for(int j=0;j<=n;++j)
                    if(k<=i||k<=j)//不能从大的转移回来
                        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    int main(){
        scanf("%d%d%d",&n,&m,&k); Floyd();
        S=n*2+2; T=S+1;
        for(int i=1;i<=n;++i)
            link(S,i,1,0),link(i+n+1,T,1,0);
        link(S,0,k,0);//最大流量限制为k
        for(int i=0;i<=n;++i)
            for(int j=i+1;j<=n;++j)
                    link(i,j+n+1,1,dis[i][j]);
        while(bfs());
        printf("%d",tC);
        return 0;
    }
  • 相关阅读:
    Android_自定义适配器
    Android_ListView
    Android_布局
    Android_基础控件
    Android_基础
    PHP框架_ThinkPHP数据库
    PHP框架_ThinkPHP基础
    PHP框架_Smarty_实现登录功能
    PHP框架_Smarty
    PHP文件上传
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10805119.html
Copyright © 2011-2022 走看看