hdu 4309 状压+最大流

本文介绍了一道关于城市间通道的最大人流问题,利用状压DP与最大流算法求解最优方案,包括通过最大人数及最小修理费用。文中详细展示了具体的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题目大意:有n个城市,每个城市有一个人数,城市与城市之间有三种通道(均为单向):隧道,公路和桥。隧道和公路都可以通过无限制的人,并且隧道可以避难ai人。桥没有修理的时候只能通过一个人次,修理之后就和公路没有差别了,修理有费用。特别注意桥不超过12条。求通过最大人数,最少修理话费。

最多只有12个。。。。直接枚举可能就过了--4000+种可能情况而已。每次输入的时候记录有多少个桥需要修 cnt++ 状压枚举的时候一定是枚举修桥之后的情况

枚举方法(状压 1<<cnt


每个状态求一次流量,然后更新答案。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 60007;
const int maxm = 65000;
const int oo = 99999999;

int idx;
int cur[maxn], pre[maxn];
int dis[maxn], gap[maxn];
int aug[maxn], head[maxn];

struct Node
{
    int u, v, w;
    int next;
}edge[maxm];

void addEdge(int u, int v, int w)
{
    edge[idx].u = u;
    edge[idx].v = v;
    edge[idx].w = w;
    edge[idx].next = head[u];
    head[u] = idx++;

    edge[idx].u = v;
    edge[idx].v = u;
    edge[idx].w = 0;
    edge[idx].next = head[v];
    head[v] = idx++;
}


int SAP(int s, int e, int n)
{
    int max_flow = 0, v, u = s;
    int id, mindis;
    aug[s] = oo;
    pre[s] = -1;
    memset(dis, 0, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    gap[0] = n; // 我觉得这一句要不要都行,因为dis[e]始终为0
    for (int i = 0; i <= n; ++i)
    {   // 初始化当前弧为第一条弧
        cur[i] = head[i];
    }

    while (dis[s] < n)
    {
        bool flag = false;
        if (u == e)
        {
            max_flow += aug[e];
            for (v = pre[e]; v != -1; v = pre[v]) // 路径回溯更新残留网络
            {
                id = cur[v];
                edge[id].w -= aug[e];
                edge[id^1].w += aug[e];
                aug[v] -= aug[e]; // 修改可增广量,以后会用到
                if (edge[id].w == 0) u = v; // 不回退到源点,仅回退到容量为0的弧的弧尾
            }
        }
        for (id = cur[u]; id != -1; id = edge[id].next)
        {   // 从当前弧开始查找允许弧
            v = edge[id].v;
            if (edge[id].w > 0 && dis[u] == dis[v] + 1) // 找到允许弧
            {
                flag = true;
                pre[v] = u;
                cur[u] = id;
                aug[v] = min(aug[u], edge[id].w);
                u = v;
                break;
            }
        }
        if (flag == false)
        {
            if (--gap[dis[u]] == 0) break; /* gap优化,层次树出现断层则结束算法 */
            mindis = n;
            cur[u] = head[u];
            for (id = head[u]; id != -1; id = edge[id].next)
            {
                v = edge[id].v;
                if (edge[id].w > 0 && dis[v] < mindis)
                {
                    mindis = dis[v];
                    cur[u] = id; // 修改标号的同时修改当前弧
                }
            }
            dis[u] = mindis + 1;
            gap[dis[u]]++;
            if (u != s) u = pre[u]; // 回溯继续寻找允许弧
        }
    }
    return max_flow;
}
int val[1010];
struct node
{
    int u,v,w,p;
}Q[1010];
//int map[300][300];
int main()
{
    int t, n, m, pi, si, ei;
    int Max, sum, source, sink, vn;
    //scanf("%d", &t);
    t=1;
    for (int cas = 1; cas <= t; ++cas)
    {
        //scanf("%d%d",&n,&m);
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            int flag=0;
            for(int i=1;i<=n;i++)
                scanf("%d",&val[i]);
          //  getchar();
            int cnt=0;
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d%d%d",&Q[i].u,&Q[i].v,&Q[i].w,&Q[i].p);
                if(Q[i].p>0)
                    cnt++;
                if(Q[i].p<0&&Q[i].w!=0)
                {
                    flag=1;
                   // cout<<"*****"<<endl;
                }
               // cout<<Q[i].p<<" ";
            }
            if(!flag)
            {
                puts("Poor Heaven Empire");
                continue;
            }
            int mm=(1<<cnt);

            int maxnum=-oo,mincost=0;
            for(int k=0;k<mm;k++)
            {
                idx = 0;
                source = 0;sink = n + 1; vn = sink + 1;
                memset(head, -1, sizeof(head));
                sum = 0;  Max = 0;
           /* for (int i = 1; i <= n; ++i)
            {
                scanf("%d %d %d", &pi, &si, &ei);
                sum += pi;
                Max = max(Max, ei);
                addEdge(source, i, pi);
                for (int j = si; j <= ei; ++j)
                {
                    addEdge(i, n + j, 1);
                }
            }  */
                //cout<<endl;
                int j=0,cost=0;
                for(int i=1;i<=n;i++)
                    addEdge(0,i,val[i]);
                for(int i=1;i<=m;i++)
                {
                    //cout<<Q[i].u<<" "<<Q[i].v<<" "<<Q[i].w<<" "<<Q[i].p<<endl;
                    if(Q[i].p<0)
                    {
                        addEdge(Q[i].u,Q[i].v,oo);
                        addEdge(Q[i].u,sink,Q[i].w);
                    }
                    else if(Q[i].p==0)
                        addEdge(Q[i].u,Q[i].v,oo);
                    else
                    {
                        //cout<<Q[i].p<<"   ****"<<endl;
                        if(k&(1<<j))
                        {
                            addEdge(Q[i].u,Q[i].v,oo);
                            cost+=Q[i].w;
                           // cout<<cost<<endl;
                        }
                        else addEdge(Q[i].u,Q[i].v,1);
                        j++;
                    }
                }
                int ans=SAP(0,sink,vn);
                if(ans>maxnum||(ans==maxnum&&cost<mincost))
                {
                    maxnum=ans;
                    mincost=cost;
                }
            }
            if(maxnum>0)
                printf("%d %d\n",maxnum,mincost);
            else printf("Poor Heaven Empire\n");
            /*for (int i = 1; i <= Max; ++i)
            {
                addEdge(n + i, sink, m);
            }
            if (SAP(source, sink, vn) == sum)
                printf("Case %d: Yes\n\n", cas);
            else printf("Case %d: No\n\n", cas);  */
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值