多校第一场:hdu4309(最大流+枚举)

53 篇文章 0 订阅

        
        题意: n个点,每个点有初始的值 ,三种 通道,1、隧道:可以用来躲避,有固定的容量,也可以用来传递。2、普通的道路,可以无限的通过。3、桥:不花费的话能通过一人,修之后可以无限通过。问最少花费最大可以隐藏人数。
       可以建立最大流模型来求解,增加一个源点S,和一个汇点T。S向每个有人的点,连一条容量为人数的边,图中普通的u->v的有向边,连一条u->v的流量为无穷的边,桥的流量则为1。对于隧道,每个隧道可以虚拟出一个点,如u->v的隧道,可以虚拟一个点x,连接u->x,x->v的流量无穷的边,和x->T的流量为隧道人数上限的边,求解最大流即可得到最大人数。
       现在考虑桥的问题,题目中说明了桥最多只有12座,故可以2^12枚举修复哪些桥,不修复的桥没有花费,连接的边流量为1,要修复的桥则计算花费,边的流量为无穷,这样进行2^12次最大流就可以得到最优解。
      for(int i=0;i<(1<<sum);i++)可以枚举出所有的情况。
      if(i&(1<<p[i]))表示枚举该桥修复。
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<string>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include<queue>
#include<cmath>
using namespace std;
const int inf=1<<30;
const int M=500,ME=3009;
const int INF=0x3f3fffff;
//******************************最小费用最大流模版(可以单独用来求最大流,输入的时候,cost=0就可以:适用于u->v,只要符合了流量限制,只要通过了就花费,还有一种是:u->v,每单位的流量必须花费对少费用(poj2159模板))
int Head[M],Next[ME],Num[ME],Flow[ME],Cap[ME],Cost[ME],vis[ME],Q[M],InQ[M],Len[M],pre_edge[M];
int top;
    void clear()
    {
        memset(Head,-1,sizeof(Head));
        memset(Flow,0,sizeof(Flow));
        memset(vis,0,sizeof(vis));
        top=0;
    }
    void addedge(int u,int v,int cap,int cost)
    {
        Next[top] = Head[u];
        Num[top] = v;
        Cap[top] = cap;
        Cost[top] = cost;
        Head[u] = top++;


        Next[top] = Head[v];
        Num[top] = u;
        Cap[top] = 0;
        Cost[top] = -cost;
        Head[v] = top++;
    }


    bool SPFA(int s,int t)
    {
        fill(Len,Len+M,INF);
        Len[s]=0;
        int head = -1,tail = -1,cur;
        Q[++head] = s;
        while(head != tail)
        {
            ++tail;
            if(tail >= M) tail = 0 ;
            cur = Q[tail];
            for(int i = Head[cur];i != -1;i = Next[i])
            {
                if(Cap[i]>Flow[i] && Len[Num[i]] > Len[cur] + Cost[i])
                {
                    Len[Num[i]] = Len[cur] + Cost[i];
                    pre_edge[Num[i]] = i;
                    if(!InQ[Num[i]])
                    {
                        InQ[Num[i]]=true;
                        ++head;
                        if(head >= M) head = 0;
                        Q[head] = Num[i];
                    }
                }
            }
            InQ[cur]=false;
        }
        return Len[t] != INF;
    }


    int solve(int s,int t)
    {
       int  ans= 0;
        while(SPFA(s,t))
        {
            int cur = t,minflow = INF;
            while(cur != s)
            {
                if(minflow > Cap[pre_edge[cur]]-Flow[pre_edge[cur]])
                    minflow = Cap[pre_edge[cur]]-Flow[pre_edge[cur]];
                cur = Num[pre_edge[cur] ^ 1];
            }
            cur = t ;
            ans+=minflow;
            while(cur != s)
            {
                Flow[pre_edge[cur]] += minflow;
                Flow[pre_edge[cur] ^ 1] -= minflow;
                vis[pre_edge[cur]]=1;
                cur = Num[pre_edge[cur] ^ 1];
            }
        }
        return ans;//最大流
         int cost;//最小费用
        for(int i=0;i<top;i++) //适用于u->v,只要符合了流量限制,只要通过了就花费,不管通过多少流量
        { 
               if(Flow[i]>0)
               cost+=Cost[i]; 
       }
    }
//****************************************************************

int n,m;
int val[M];
int u[1509],v[1509],w[1509],p[1509];
int uu[15],vv[15],ww[15];


int main()
{
  while(scanf("%d%d",&n,&m)!=EOF)
  {
      clear();
      int start=0;
      int end=n+25;
      int b=n+1;
      int k=0;


      for(int i=1;i<=n;i++)
      {
         scanf("%d",&val[i]);
         addedge(start,i,val[i],0);
      }


      for(int j=1;j<=m;j++)
      {
        scanf("%d%d%d%d",&u[j],&v[j],&w[j],&p[j]);
        if(p[j]==0)
        {
          addedge(u[j],v[j],inf,0);
        }
        else if(p[j]<0)
        {
          addedge(u[j],b,inf,0);
          addedge(b,v[j],inf,0);
          addedge(b++,end,w[j],0);
        }
        else
        {
          addedge(u[j],v[j],inf,0);
          uu[k]=u[j];
          vv[k]=v[j];
          ww[k]=w[j];
          k++;
          p[k]=k;
        }
      }


      int an=solve(start,end);
      int mm=1<<k;
      int co=inf;
      
    for(int i=0;i<mm;i++)
     {
        clear();
        b=n+1;
        for(int j=1;j<=n;j++)
        {
         addedge(start,j,val[j],0);
        }


       for(int j=1;j<=m;j++)
       {
        if(p[j]==0)
        {
          addedge(u[j],v[j],inf,0);
        }
        else if(p[j]<0)
        {
          addedge(u[j],b,inf,0);
          addedge(b,v[j],inf,0);
          addedge(b++,end,w[j],0);
        }
        else
        {
          addedge(u[j],v[j],1,0);
          if((1<<(p[j]-1))&i)
          addedge(u[j],v[j],inf,0);
        }
       }
       int tmp=solve(start,end);
       if(tmp==an)
       {
           int r=0;
           for(int t=0;t<k;t++)
           {
             if(i&(1<<t))
             r+=ww[t];
           }
           if(r<co)
           co=r;
       }
      }
        if(an==0)
        puts("Poor Heaven Empire");
        else
        printf("%d %d\n",an,co);
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值