hdu5294(最大流+最短路)Tricks Device

21 篇文章 0 订阅
19 篇文章 0 订阅

这道题刚开始想了很久,而且之前一直不明白dicnic既然已经求了阻塞流,为什么还需要反向弧呢,现在如图:
这里写图片描述
2和3是一层,4和5是一层,然后你要是走1->2->4->6的话,如果没有反向弧,你会发现最大流是2,没有增广路了,而当有了反向弧之后,你还可以走1->3->4->2->5->6,以前总听别人说有反向弧,然而一直没找到合适的例子,今天终于让我找到了,非常开心
下面说一下今天这道题,这个题也是听了学姐的思路才会的,首先是第一问,求最少去掉几条边使其不能构成最短路,最少是说最小割即最大流,然后分别求以1为原点和以n为原点的最短路,然后给的边,如果两点分别到1和n的最短路加上两点间距离等于1到n的最短路,那么这条边就有可能是最短路上的边,把它加到一个数组里,去进行最大流,需要强调的是,可能一条边的起点u,终点v,u到1的距离比v到1的距离远,那么交换他们,然后跑一边最大流就行了,第二问是求最多去掉几条变仍然能构成最短路,那么此时把边都赋成1,跑一边最短路,用总边数一减就ok了,
需要补充的一点是,两个add函数如果同名,变量的数目不一样不发生冲突,因为这是重载
代码如下:

#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn=2005;
const int INF=0x3f3f3f3f;
struct Edge
{
    int v;
    int cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost) {}
};
vector <Edge>E[maxn];
void add(int u,int v,int w)
{
    E[u].push_back(Edge(v,w));
}
bool vis [maxn];
int cnt[maxn];
int dist [maxn];
bool spfa(int start,int n)
{
    memset(vis,false,sizeof(vis));
    for(int i=1; i<=n; i++)
        dist[i]=INF;
    vis[start]=true;
    dist[start]=0;
    queue<int>que;
    while(!que.empty())
        que.pop();
    que.push(start);
    memset(cnt,0,sizeof(cnt));
    cnt[start]=1;
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=0;i<E[u].size();i++)
        {
            int v=E[u][i].v;
            if(dist[v]>dist[u]+E[u][i].cost)
            {
                dist[v]=dist[u]+E[u][i].cost;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                    if(++cnt[v]>n)
                        return false;
                }
            }
        }
    }
    return true;
}
/
const int oo=1e9;
const int mm=121111;
const int mn=2999;
int node,src,dest,edge;
int ver[mm],flow[mm],_next[mm];
int head[mn],work[mn],dis[mn],q[mn];
void prepare(int _node,int _src ,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;++i)
        head[i]=-1;
    edge=0;
}
void addedge(int u,int v,int c)
{
    ver[edge]=v,flow[edge]=c,_next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,_next[edge]=head[v],head[v]=edge++;
}
bool Dicnic_bfs()
{
    int i,u,v,l,r=0;
    for(i=0;i<node;++i)
        dis[i]=-1;
    dis[q[r++]=src]=0;
    for(l=0;l<r;++l)
        for(i=head[u=q[l]];i>=0;i=_next[i])
        if(flow[i]&&dis[v=ver[i]]<0)
        {
            dis[q[r++]=v]=dis[u]+1;
            if(v==dest)
                return 1;
        }
        return 0;
}
int Dicnic_dfs(int u,int exp)
{
    if(u==dest)
        return exp;
    for(int &i=work[u],v,tmp;i>=0;i=_next[i])
        if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dicnic_dfs(v,min(exp,flow[i])))>0)
    {
        flow[i]-=tmp;
        flow[i^1]+=tmp;
        return tmp;
    }
  return 0;
}
int Dicnic_flow()
{
    int i,ret=0,delta;
    while(Dicnic_bfs())
    {
        for(i=0;i<node;++i)
            work[i]=head[i];
        while(delta=Dicnic_dfs(src,oo))
            ret+=delta;
    }
    return ret;
}
int u[60005],v[60005],w[60005];
int b[mn][2],dist2[mn];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<=n;i++)
            E[i].clear();
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            add(u[i],v[i],w[i]);
            add(v[i],u[i],w[i]);
        }
        spfa(1,n);
        memcpy(dist2,dist,sizeof(dist));
        //memset(dist,0,sizeof(dist));
        spfa(n,n);
        int k=0;
        for(int i=0;i<m;i++)
        {
            if(dist2[u[i]]>dist2[v[i]])
                swap(u[i],v[i]);
            if(dist[v[i]]+w[i]+dist2[u[i]]==dist2[n])
            {
                b[k][1]=u[i];
                b[k][0]=v[i];
                k++;
            }
        }
        prepare(n+1,1,n);

        for(int i=0;i<k;i++)
        {
            addedge(b[i][1],b[i][0],1);
        }
        int sum=Dicnic_flow();
        for(int i=0;i<n;i++)
            E[i].clear();
        for(int i=0;i<k;i++)
        {
            add(b[i][1],b[i][0],1);
            add(b[i][0],b[i][1],1);
        }
        spfa(1,n);
        printf("%d %d\n",sum,m-dist[n]);
    }
    return 0;
}

第二遍写这个题,费死劲了,还是知识掌握的不牢,代码能力太差,这次用的是迪杰斯特拉的模板,感觉还可以


#include <iostream>
#include <string.h>
#include <queue>
using namespace std;

const int oo=1e9;
/**oo 表示无穷大*/
const  int maxm=200005;
/**maxm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/
const  int maxn=2999;
/**maxn 表示点的最大数量*/
int node,src,dest,edge;
/**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/
int ver[maxm],flow[maxm],_next[maxm];
/**ver 边指向的节点,flow 边的容量 ,_next 链表的下一条边*/
int head[maxn],work[maxn],dis[maxn],q[maxn];
void prepare(int _node,int _src ,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;++i)
        head[i]=-1;
    edge=0;
}
void addedge(int u,int v,int c)
{
    ver[edge]=v,flow[edge]=c,_next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,_next[edge]=head[v],head[v]=edge++;
}
bool Dicnic_bfs()
{
    int i,u,v,l,r=0;
    for(i=0;i<node;++i)
        dis[i]=-1;
    dis[q[r++]=src]=0;
    for(l=0;l<r;++l)
        for(i=head[u=q[l]];i>=0;i=_next[i])
        if(flow[i]&&dis[v=ver[i]]<0)
        {
            dis[q[r++]=v]=dis[u]+1;
            if(v==dest)
                return 1;
        }
        return 0;
}
int Dicnic_dfs(int u,int exp)
{
    if(u==dest)
        return exp;
    for(int &i=work[u],v,tmp;i>=0;i=_next[i])
        if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dicnic_dfs(v,min(exp,flow[i])))>0)
    {
        flow[i]-=tmp;
        flow[i^1]+=tmp;
        return tmp;
    }
  return 0;
}
int Dicnic_flow()
{
    int i,ret=0,delta;
    while(Dicnic_bfs())
    {
        for(i=0;i<node;++i)
            work[i]=head[i];
        while(delta=Dicnic_dfs(src,oo))
            ret+=delta;
    }
    return ret;
}


//

struct Edge
{
    int to,next,w;
} edges[maxm];
int tot;
int head2[maxn];
void init()
{
    tot = 0;
    memset(head2,-1,sizeof(head2));
}
void add(int u,int v,int w)
{
    edges[tot].to=v;
    edges[tot].w=w;
    edges[tot].next=head2[u];
    head2[u]=tot++;
   // cout<<head2[u]<<endl;
}
bool vis[maxn];
struct sa
{
    int u,w;
    sa(int u,int d):u(u),w(d) {}
};
bool operator < (sa a,sa b)
{
    return a.w > b.w;
}
void  dijk(int s,int n)
{
    memset(vis,0,sizeof(vis));
    priority_queue<sa> que;
    for(int i=0; i<=n; i++) dis[i]=oo;
    dis[s]=0;

    que.push(sa(s,0));
    while(!que.empty())
    {
        sa tmp = que.top();
        que.pop();
        int u=tmp.u;
        if(vis[u]) continue;
        vis[u]=true;
        for(int e=head2[u]; e!=-1; e=edges[e].next)
        {
            int v=edges[e].to,w=edges[e].w;
            if(vis[v]) continue;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                que.push(sa(v,dis[v]));
            }
        }
    }

}
int u[60006],v[60006],w[60006];
int b[maxn][2],dis2[maxn];
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        init();
        memset(u,0,sizeof(u));
        memset(v,0,sizeof(v));
        memset(w,0,sizeof(w));
        for(int i=0;i<m;i++)
        {
             cin>>u[i]>>v[i]>>w[i];
             add(u[i],v[i],w[i]);
             add(v[i],u[i],w[i]);

        }
        dijk(1,n);

        for(int i=0;i<=n;i++)
            dis2[i]=dis[i];
        memset(dis,0,sizeof(dis));
        dijk(n,n);
         int k=0;
        for(int i=0;i<m;i++)
        {
//我觉得这个地方一定要注意一下,一定要把起点和终点的顺序弄对,
//如果两个距离反了,交换的应该是两个点而不是距离
            if(dis2[u[i]]>dis2[v[i]])
                swap(u[i],v[i]);

            if(dis[v[i]]+w[i]+dis2[u[i]]==dis[1])
            {
                b[k][1]=u[i];
                b[k][0]=v[i];
                k++;
            }
        }
          prepare(n+1,1,n);
          for(int i=0;i<k;i++)
          {
              addedge(b[i][1],b[i][0],1);
          }

        int sum=Dicnic_flow();
        memset(dis,0,sizeof(dis));
        for(int i=0;i<k;i++)
            {
                add(b[i][1],b[i][0],1);
               // add(b[i][0],b[i][1],1);
            }
        dijk(1,n);
        cout<<sum<<" "<<m-dis[n]<<endl;

    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值