hdu 5352 MZL's City (最小费用最大流)

题目链接:哆啦A梦传送门

题意:n座城市,m年修复这个国家,每年有如下3种操作之中一个,k表示每一年最多修复城市k座。现在所有的城市和道路都被摧毁了。有m年时间修复这个国家。

操作分三种:

1 x:最多修复与x相连通的城市k座(包括x)。

2 x y:修复城市x与城市y的双向道路。

3 p:接着有p对 (x,y),表示摧毁城市x与城市y的双向道路。

输出:能修复最多多少个城市?接着输出每次1操作时,能修复多少个城市?(字典序要最小)

Sample Input

1

5 6 2

2 1 2

2 1 3

1 1

1 2

3 1 1 2

1 2

Sample Output

3

0 2 1

最多修复3座城市,第四年不修复,第五年修复城市1和城市3,第六年修复城市2。

 

题解:对于求最多修复多少个城市?我们可以用最大流就可以解决。

只需建模:对于m年中的操作1,我们可以与x相连城市的联通块一起看成是一个点,

然后我们将源点与每个联通块建边,权值为k;

紧接着我们将每个联通块与该联通块中的城市建边,权值为1;

最后我们将每个城市与汇点建边,权值为1。

最后跑个dinic就行了,但是题目还要输出每个操作2时,能修复多少个城市,保证字典序最小。这是单单跑个dinic就解决不了了。但是我们可以给每个源点到每个联通块加一个费用(费用逐级递减),其它费用为0,因为这样我们跑费用流会尽量让后面的操作去修复城市,这样就可以使得字典序最小。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=1010;
const int INF=0x3f3f3f3f;

struct edge{
    int u,v,flow,cost,next;
    edge(){}
    edge(int _u,int _v,int _flow,int _cost,int _next){
        u=_u;v=_v;flow=_flow;cost=_cost;next=_next;
    }
}e[1000000];

int head[N],cnt;
//queue<int> que;

void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
}

void add(int x,int y,int z,int c)
{
    e[cnt]=edge(x,y,z,c,head[x]);
    head[x]=cnt++;
    e[cnt]=edge(y,x,0,-c,head[y]);
    head[y]=cnt++;
}

bool vis[N];
int dis[N],pre[N];

bool spfa(int s,int t)
{
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));

    dis[s]=0;
    vis[s]=true;
//    while(!que.empty()) que.pop();
    queue<int> que;
    que.push(s);

    while(!que.empty())
    {
        int x=que.front();
        que.pop();
        vis[x]=false;

        for(int i=head[x];i!=-1;i=e[i].next){
            if(e[i].flow){
                int y=e[i].v;
                if(dis[x]+e[i].cost<dis[y]){
                    dis[y]=dis[x]+e[i].cost;
                    pre[y]=i;
                    if(!vis[y]){
                        que.push(y);
                        vis[y]=true;
                    }
                }
            }
        }

    }
     return pre[t]!=-1;

}

int costflow(int s,int t)///源点x,汇点t,需要的总流x
{
    int ret=0;
    int max_flow=0;

    while(spfa(s,t))
    {
        int min_flow=INF;
        for(int i=pre[t];i!=-1;i=pre[e[i].u]){
            min_flow=min(e[i].flow,min_flow);
        }
        for(int i=pre[t];i!=-1;i=pre[e[i].u]){
            e[i].flow-=min_flow;
            e[i^1].flow+=min_flow;
        }
        ret+=dis[t]*min_flow;
        max_flow+=min_flow;
    }


    return max_flow;
}
int n,m,k;

int mp[210][210],tot;
bool flag[510][510];
vector<int> vec[510];

void dfs(int fa,int u)///搜索该连通块相连的城市,存储在vec
{
    flag[fa][u]=1;
    vec[fa].push_back(u);
    for(int i=1;i<=n;i++)
    {
        if(flag[fa][i]||!mp[u][i]) continue;
        dfs(fa,i);
    }
}


int main()
{
    int ncase;
    scanf("%d",&ncase);

    while(ncase--)
    {

        init();
        scanf("%d%d%d",&n,&m,&k);

        memset(flag,0,sizeof(flag));
        memset(mp,0,sizeof(mp));
        for(int i=0;i<=m+1;i++)
            vec[i].clear();
            
       tot=0;///表示联通块的数量

        for(int i=1;i<=m;i++)
        {
            int op,x,y;
            scanf("%d",&op);
            if(op==1){
                scanf("%d",&x);
                tot++;
                dfs(tot,x);

            }
            else if(op==2)
            {
                scanf("%d%d",&x,&y);
                mp[x][y]=mp[y][x]=1;
            }
            else{
                int num;
                scanf("%d",&num);
                for(int i=1;i<=num;i++){
                    scanf("%d%d",&x,&y);
                    mp[x][y]=mp[y][x]=0;
                }
            }
        }

        int s=0,t=n+tot+1;

        for(int i=1;i<=tot;i++) ///源点到联通块建边,权值为k,费用递减
        {
            add(s,i,k,m-i);
        }
        for(int i=1;i<=n;i++) ///每个城市与汇点建边,权值为1,费用为0
            add(i+tot,t,1,0);

        for(int i=1;i<=tot;i++)
        {
            int len=vec[i].size();
            for(int j=0;j<len;j++){ ///将每个联通块与该联通块内的城市建边,权值为1,费用为0
                add(i,vec[i][j]+tot,1,0);
            }
        }
        printf("%d\n",costflow(s,t)); ///跑一个费用流
        for(int i=1;i<2*tot-1;i+=2){ ///输出每条源点到各个联通块的反向流量边(表示该边用了多少)
            printf("%d ",e[i].flow);
        }
        printf("%d\n",e[2*tot-1].flow);


    }
    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值