hdu - 4309 - Seikimatsu Occult Tonneru - 网络流

http://acm.hdu.edu.cn/showproblem.php?pid=4309

题意: n个点,每个点有初始的值 ,三种 通道,1、隧道:可以用来躲避,有固定的容量,也可以用来传递。2、普通的道路,可以无限的通过。3、桥(最多有12座):不花费的话能通过一人,修之后可以无限通过。问最少花费最大可以隐藏人数。

解:

     网络流 + 枚举

官方题解:

先不考虑可以修复的桥的性质,则可以将模型简化为n个点的人通过有通过人数上限的有向边,到达一些有人数上限的特殊的边(隧道)。
可以建立最大流模型来求解,增加一个源点S,和一个汇点T。S向每个有人的点,连一条容量为人数的边,图中普通的u->v的有向边,连一条u->v的流量为无穷的边,桥的流量则为1。对于隧道,每个隧道可以虚拟出一个点,如u->v的隧道,可以虚拟一个点x,连接u->x,x->v的流量无穷的边,和x->T的流量为隧道人数上限的边,求解最大流即可得到最大人数。
现在考虑桥的问题,题目中说明了桥最多只有12座,故可以2^12枚举修复哪些桥,不修复的桥没有花费,连接的边流量为1,要修复的桥则计算花费,边的流量为无穷,这样进行2^12次最大流就可以得到最优解。

#include <cstdio>
#include <cstring>
const int MAXN = 205;
const int MAXM = 2505;
const int INF = 1000000000;

struct Edge
{
    int u, v, next, flow;
}edge[MAXM], redge[MAXM];
int edgeNumber, head[MAXN], rhead[MAXN];
int source = MAXN - 1;
int destination = MAXN - 2;
int depth[MAXN];

inline int min(int x, int y)
{
    return x < y ? x : y;
}

void addEdgeSub(int u, int v, int flow)
{
    edge[edgeNumber].u = u;
    edge[edgeNumber].v = v;
    edge[edgeNumber].flow = flow;
    edge[edgeNumber].next = head[u];
    head[u] = edgeNumber ++;
}

void addEdge(int u, int v, int flow)
{
    addEdgeSub(u, v, flow);
    addEdgeSub(v, u, 0);
}

int n, m;
int bridgePosition[MAXN];
int bridgeCost[MAXN];
int bridgeNumber;

bool bfs(int start, int end)
{
    int front = 0, rear = 0;
    int queue[MAXN];
    memset(depth, -1, sizeof(depth));
    queue[front++] = start;
    depth[start] = 0;
    while(rear < front)
    {
        int k = queue[rear++];
        for(int i=head[k];i!=-1;i=edge[i].next)
        {
            int to = edge[i].v;
            if(-1 == depth[to] && edge[i].flow > 0)
            {
                depth[to] = depth[k] + 1;
                queue[front++] = to;
            }
        }
    }
    return -1 != depth[end];
}

int dinic(int start, int end, int sum)
{
    if(start == end)
    {
        return sum;
    }
    int temp = sum;
    for(int i=head[start];i!=-1 && sum;i=edge[i].next)
    {
        if(edge[i].flow > 0 && depth[edge[i].v] == depth[start] + 1)
        {
            int a = dinic(edge[i].v, end, min(sum, edge[i].flow));
            edge[i].flow -= a;
            edge[i^1].flow += a;
            sum -= a;
        }
    }
    return temp - sum;
}

int maxFlow(int start, int end)
{
    int result = 0;
    while(bfs(start, end))
    {
        result += dinic(start, end, INF);
    }
    return result;
}

int main()
{
    int u, v, w, p;
    while(~scanf("%d%d", &n, &m))
    {
        int pointNumber = n + 1;
        edgeNumber = 0;
        bridgeNumber = 0;
        memset(head, -1, sizeof(head));
        for(int i=1;i<=n;++i)
        {
            scanf("%d", &w);
            addEdge(source, i, w);
        }
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d%d",&u,&v,&w,&p);
            if(p < 0)
            {
                addEdge(u, pointNumber, INF);
                addEdge(pointNumber, v, INF);
                addEdge(pointNumber, destination, w);
                ++ pointNumber;
            }
            else if(p == 0)
            {
                addEdge(u, v, INF);
            }
            else
            {
                bridgePosition[bridgeNumber] = edgeNumber;
                bridgeCost[bridgeNumber] = w;
                addEdge(u, v, 1);
                ++ bridgeNumber;
            }
        }
        memcpy(redge, edge, sizeof(redge));
        memcpy(rhead, head, sizeof(rhead));
        int minCost = INF, maxPeople = - INF;
        for(int i=0;i<(1<<bridgeNumber);++i)
        {
            memcpy(edge, redge, sizeof(edge));
            memcpy(head, rhead, sizeof(head));
            int cost = 0;
            for(int j=0;j<bridgeNumber;++j)
            {
                if(i&(1 << j))
                {
                    cost += bridgeCost[j];
                    edge[bridgePosition[j]].flow = INF;
                }
            }
            int people = maxFlow(source, destination);
            if(people > maxPeople)
            {
                maxPeople = people;
                minCost = cost;
            }
            else if(people == maxPeople)
            {
                minCost = min(minCost, cost);
            }
        }
        if(maxPeople > 0)
        {
            printf("%d %d\n", maxPeople, minCost);
        }
        else
        {
            printf("Poor Heaven Empire\n");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值