Pollutant Control_usaco 4.4_网络流

52 篇文章 0 订阅
31 篇文章 0 订阅

Description


  你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证坏牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。
{批注:先前不知是哪个写了一个要不得的翻译,这才是正常的翻译,读起来也觉得比较健康——Cow-Tsc} {修改下,更现实——pyh119}

Input


(file milk6.in) 第一行: 两个整数N(2<=N<=32)、M(0<=M<=1000), N表示仓库的数目,M表示运输卡车的数量。仓库1代 表发货工厂,仓库N代表有三聚氰胺的牛奶要发往的零售商。 第2..M+1行: 每行3个整数Si,Ei,Ci。其中Si,Ei表示这 辆卡车的出发仓库,目的仓库。Ci(0 <= C i <= 2,000,000) 表示让这辆卡车停止运输的损失。

Output


(file milk6.out) 第1行两个整数c、t,c表示最小的损失,T表示要停止的最少卡车数。接下来t 行表示你要停止哪几条线路。如果有多种方案使损失最小,输出停止的线路最少的方案。如果仍然还有相同的方案,请选择开始输入顺序最小的。

Analysis


本来想用邻接表的但是不好打啊毕竟关系到删边什么的

首先要知道最大流=最小割(证明略)
先跑一次最大流,然后枚举删掉一条边分别跑m次最大流,如果此时最大流+删去的边的边权=原本最大流,那么可以肯定这条边在最小割集合中
在做之前要按边权降序排序,这样可以删掉最少的边

Code


/*
ID:wjp13241
PROG:milk6
LANG:C++
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fil(x,t) memset(x,t,sizeof(x))
#define STP system("pause")
#define min(x,y) x<y?x:y
#define max(x,y) x>y?x:y
#define INF 0x7f7f7f7f
#define E 1021*2+1
#define N 33
using namespace std;
struct edge{int x,y,w,id;}e[E];
int c[N][N],bac[N][N],vis[N],p[E],cnt=0;
int add(int x,int y,int w){c[x][y]+=w;bac[x][y]+=w;e[++cnt]=(edge){x,y,w,cnt};}
int cmp(edge x,edge y){return x.w>y.w||x.w==y.w&&x.id<y.id;}
int rec(int n)
{
    fo(i,1,n)
        fo(j,1,n)
            c[i][j]=bac[i][j];
}
int find(int x,int n,int mn)
{
    vis[x]=1;
    if (x==n)
        return mn;
    fo(i,1,n)
        if (c[x][i]>0&&!vis[i])
        {
            int d=find(i,n,min(mn,c[x][i]));
            if (d!=0)
            {
                c[x][i]-=d;
                c[i][x]+=d;
                return d;
            }
        }
    return 0;
}
int main()
{
    freopen("milk6.in","r",stdin);
    freopen("milk6.out","w",stdout);
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    fil(c,0);
    fo(i,1,m)
    {
        int x,y,w;
        cin>>x>>y>>w;
        add(x,y,w);
    }
    rec(n);
    sort(e+1,e+cnt+1,cmp);
    int mxFlow=0;
    fil(vis,0);
    while (int tmp=find(1,n,INF))
    {
        fil(vis,0);
        mxFlow+=tmp;
    }
    cout<<mxFlow<<" ";
    int ans=0;
    fo(i,1,cnt)
    {
        rec(n);
        c[e[i].x][e[i].y]-=e[i].w;
        int tot=0;
        fil(vis,0);
        while (int tmp=find(1,n,INF))
        {
            fil(vis,0);
            tot+=tmp;
        }
        if (tot+e[i].w==mxFlow)
        {
            ans++;
            p[e[i].id]=1;
            mxFlow-=e[i].w;
            bac[e[i].x][e[i].y]-=e[i].w;
        }
    }
    cout<<ans<<endl;
    fo(i,1,m)
        if (p[i])
            cout<<i<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值