hdu -4309 Seikimatsu Occult Tonneru 网络流(佛系的复杂度...)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4309

 

题意:

        给你n个城市,每座城市里会有一些人,m条单向边 u v w type ,边会出现三种,第一种是隧道type<0的时候,隧道最多20条,w是这个隧道里可以藏的人,第二种是普通的大道type==0的时候,那么w就作废,第三种是古桥type>0的时候,古桥在不修缮的时候最多只能通过一个人,但是如果你花费w的值将它修缮,那么就可以通过无限多个人,现在发生灾难大家都要逃向隧道躲避,问你在不计修缮成本的情况下最多能通过多少人,在保证人数还是这么多的情况下费用可以减少到多少。

做法:

       一道复杂度让我很迷的网络流,明明理论上dinic网络流复杂度应该是n方m,这里还加上了状压的枚举要乘上(1<<12),因为告诉了你古桥的名字独一无二是十二星座中的一个,这样的话1e10应该是会超的,只能说数据水了...或者是没卡你这个算法。我还想着用费用流做来着。。状压我想到了不敢做啊。。。唉网络流真是佛系

       因为告诉你两个城市之间的路径唯一,所以我就把隧道里可以存的人都放在了出来的城市上,这样的话点就最多是101个,边最多是(12+100+1000)*2那么多条。源点向城市连边,容量为人数,将古桥的边的下标进行记录,在枚举的时候直接改掉会快一点,其他的也稍微优化过了是700ms的样子。也是有点好奇400ms的人都是怎么过的。


#include<bits/stdc++.h>
using namespace std;
const int Ni = 120;
const int MAX = 1<<26;
const int maxm=2500;
struct Edge{
    int u,v,c;
    int next;
}edge[maxm],e[maxm];
int n,m,cnt,head[Ni],d[Ni],sp,tp;//原点,汇点
//理论复杂度n2*m

void add(int u,int v,int c){
    edge[cnt].u=u; edge[cnt].v=v; edge[cnt].c=c;
    edge[cnt].next=head[u]; head[u]=cnt++;

    edge[cnt].u=v; edge[cnt].v=u; edge[cnt].c=0;
    edge[cnt].next=head[v]; head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next){
            int u=edge[i].v;
            if(d[u]==-1 && edge[i].c>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && d[u]==d[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}

int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
struct spedge{
    int id,cost;
}se[20];
int nse,x;
void init(){
    cnt=0; nse=0;
    memset(head,-1,sizeof(head));
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        sp=0,tp=n+1;
        for(int i=1;i<=n;i++) scanf("%d",&x),add(sp,i,x);
        for(int j=1;j<=m;j++){
            int fr,to,co,ty;
            scanf("%d%d%d%d",&fr,&to,&co,&ty);
            if(ty>0){
                se[nse].id=cnt,se[nse].cost=co;
                nse++;
                add(fr,to,1);
            }
            else add(fr,to,MAX);
            if(ty<0) add(fr,tp,co);
        }

        memcpy(e,edge,sizeof(edge));
        int ansflow=0,anscost=MAX;
        for(int i=0;i<(1<<nse);i++){
            memcpy(edge,e,sizeof(edge));
            int flow=0,cost=0;
            for(int j=0;j<nse;j++){
                if(i&(1<<j)) {
                    cost+=se[j].cost;
                    edge[se[j].id].c=MAX;
                }
            }
            flow=dinic(sp,tp);
            if(flow>ansflow){
                ansflow=flow; anscost=cost;
            }
            else if(flow==ansflow&&cost<anscost){
                anscost=cost;
            }
        }
        if(ansflow==0) printf("Poor Heaven Empire\n");
        else printf("%d %d\n",ansflow,anscost);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值