ccf (201812-5 ) 管道清洁

 

目录

题目描述

思路

有图示与解释的样例输入1

样例输入2

样例输入3

样例输入4

数据范围

代码


 

 

题目描述

在这里插入图片描述

思路

需要清洁的管道下界为1,

不需要清洁的管道下界为0,

可重复经过的管道上界为正无穷,

不可重复经过的管道上界为1。


算是最小费用流的裸题

最小费跟普通最大流的区别就是遍历图时不能直接bfs, 而是找最短路(费用为权).

 

 

在找增广路(最短路)时, 计算各边最小残量, flow[v]=min(flow[u],e[i].f);

返回的最短路径长度,为总流可改进量

然后用最小残量来更新增广路上各边流量, 并把最小残量加进当前流结果.

同时更新最小费, 最小费 += 最小残量*最短路长.

 

 

 

 

有图示与解释的样例输入1

3 0 1
4 4
1 2 A
2 3 B
3 4 C
4 1 D
5 7
1 2 B
2 3 B
3 1 D
2 4 A
4 3 C
1 5 C
5 2 D
5 7
1 2 B
2 3 B
3 1 C
2 4 A
4 3 D
1 5 C
5 2 D

样例输出

4
-1
8

 

 

样例说明

样例输入2

6 0 0
5 15
1 3 A
1 3 A
1 5 A
2 3 A
2 4 A
2 5 A
3 1 A
3 2 A
3 4 A
4 1 A
4 4 A
4 5 A
5 2 A
5 2 A
5 3 A
5 15
1 3 B
1 4 B
1 4 B
1 5 B
2 2 B
2 3 B
3 2 B
3 5 B
4 1 B
4 1 B
4 1 B
4 4 B
4 4 B
5 1 B
5 4 B
5 15
1 1 B
1 2 B
1 2 B
1 2 B
1 3 B
1 5 B
2 1 A
2 1 B
2 1 B
2 2 B
2 2 B
3 3 B
3 4 B
4 1 B
5 2 B
5 15
1 1 C
1 3 A
1 3 A
1 5 C
2 1 A
2 1 C
2 3 C
2 4 C
3 2 A
3 2 A
3 2 C
3 2 C
4 1 C
4 4 C
5 3 C
5 15
1 1 B
1 3 B
1 4 D
1 4 D
1 5 B
1 5 D
2 1 B
3 4 D
4 1 D
4 1 D
4 2 D
4 5 D
5 1 B
5 1 B
5 4 D
5 15
1 1 D
1 1 D
1 4 C
1 5 B
2 5 B
3 1 D
3 4 D
4 1 D
4 3 D
4 3 D
4 4 D
4 5 D
5 1 B
5 2 B
5 4 D

样例输出

0
0
0
0
0
0

样例输入3

6 0 0
5 15
1 3 A
1 5 A
2 1 A
2 3 A
2 4 A
2 5 A
3 2 A
3 3 A
3 4 A
3 5 A
4 5 A
4 5 A
5 1 A
5 3 A
5 5 A
5 15
1 2 B
1 4 B
1 4 B
2 1 B
2 2 B
2 3 B
2 5 B
3 1 B
3 2 B
4 1 B
4 4 B
4 4 B
5 1 B
5 3 B
5 4 B
5 15
1 1 A
1 1 A
2 5 A
2 5 B
3 2 A
3 2 A
3 2 B
3 2 B
4 1 B
4 3 A
4 4 B
4 5 B
4 5 B
5 3 B
5 4 A
5 15
1 1 A
1 1 A
1 2 A
1 2 A
1 5 A
2 3 A
3 1 A
3 2 C
3 3 A
4 5 C
4 5 C
4 5 C
5 2 C
5 3 C
5 5 A
5 15
1 2 B
2 3 B
2 4 B
2 5 D
3 3 D
3 5 B
4 1 B
4 3 B
4 3 B
4 4 B
4 4 D
5 1 D
5 4 B
5 4 D
5 5 B
5 15
1 3 C
1 4 B
1 5 C
2 3 B
2 5 D
3 1 A
3 2 A
3 3 B
3 4 B
3 5 C
4 3 A
4 3 B
5 2 C
5 3 B
5 3 C

样例输出

0
-1
-1
0
-1
0

 

样例输入4

6 0 1
5 15
1 1 A
1 2 A
1 4 A
1 4 A
1 4 A
2 1 A
2 4 A
3 4 A
4 1 A
4 1 A
4 2 A
4 3 A
4 4 A
4 5 A
5 1 A
5 15
1 3 B
1 3 B
1 4 B
2 1 B
3 1 B
3 1 B
3 4 B
3 5 B
4 3 B
4 4 B
4 5 B
5 2 B
5 3 B
5 5 B
5 5 B
5 15
1 1 B
1 5 B
1 5 B
1 5 B
2 1 B
3 1 B
3 4 B
4 2 B
4 3 A
4 4 B
5 1 B
5 4 B
5 4 B
5 5 B
5 5 B
5 15
1 2 A
1 5 A
2 1 A
2 2 A
2 4 C
3 4 C
4 2 C
4 2 C
4 3 C
4 4 C
4 4 C
4 4 C
4 5 C
5 1 A
5 4 C
5 15
1 2 B
1 3 D
1 5 B
2 2 B
2 3 D
2 4 D
3 1 B
3 1 B
3 2 D
3 3 D
3 3 D
3 5 D
4 3 D
5 1 B
5 3 D
5 15
1 3 B
1 3 C
2 1 D
3 1 D
3 1 D
3 2 D
3 3 D
3 3 D
3 4 D
3 5 B
3 5 D
4 3 D
5 3 B
5 3 B
5 5 D

样例输出

15
15
16
5
8
6

数据范围

代码

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 222;
struct node
{
    int u,v,f,w,nxt;
    node(){}
    node(int a,int b,int c,int d,int e):u(a),v(b),f(c),w(d),nxt(e){}
}e[N*N];
int cnt=0,n,m,s0,t0,T,baozi,num=0,sum=0;
int head[N],d[N];
void add_edge(int u,int v,int f,int w)
{
    e[cnt] = node(u,v,f,w,head[u]);
	head[u] = cnt++;
	e[cnt] = node(v,u,0,-w,head[v]);
	head[v] = cnt++;
}
int pre[N],flow[N],dis[N];
bool vis[N];
int spfa()
{
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;
    q.push(s0);
    flow[s0]=inf;
    vis[s0]=1;
    dis[s0]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            if(e[i].f&&dis[v]>dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                //pre数组记录流入v点的边是i
                pre[v]=i;
                flow[v]=min(flow[u],e[i].f);
                if (!vis[v])
                    vis[v]=1,q.push(v);
            }
        }
    }
    if (pre[t0]==-1) return -1;
    return flow[t0];
}
int minCostMaxflow()
{
    int fw=0;
    int ans=0;
    int d;
    while((d=spfa())!=-1)
    {
        fw+=d;
        //最小残量加进当前流结果
        ans+=dis[t0]*d;
        //最小费 += 最小残量*最短路长
        int v=t0;
        while(v!=s0)
        {
            e[pre[v]].f-=d;
            e[pre[v]^1].f+=d;
            v=e[pre[v]].u;
        }
    }

    if (fw!=sum) return -1;
    else return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int s;
    cin>>T>>s>>baozi;
    while(T--)
    {
        cin>>n>>m;
        s0=0;//虚拟源点
        t0=n+1;//虚拟汇点
        cnt=0;//边的条数
        sum=0;//需要清扫的道路数目
        num=0;//根据需要清扫的道路数目得知必须消耗的包子数
        memset(head,-1,sizeof(head));
        memset(d,0,sizeof(d));
        int u,v;
        char c;
        while(m--)
        {
            cin>>u>>v>>c;
            if(c=='A')
            {
                add_edge(u,v,inf,baozi);
                d[u]--;
                d[v]++;
                num+=baozi;
            }
            else if(c=='B')
            {
                d[u]--;
                d[v]++;
                num+=baozi;
            }
            else if(c=='C')
                add_edge(u,v,inf,baozi);
            else
                add_edge(u,v,1,baozi);
        }
        for(int i=1;i<=n;++i)
        {
            if(d[i]>0)
                sum+=d[i],
                add_edge(s0,i,d[i],0);
            else if (d[i]<0)
                add_edge(i,t0,-d[i],0);
        }
        cout<<minCostMaxflow()+num<<endl;
    }

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值