Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证坏牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。
【输入格式】
第一行: 两个整数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) 表示让这辆卡车停止运输的损失。
【输出格式】
两个整数C、T:C表示最小的损失,T表示在损失最小的前提下,最少要停止的卡车数。
【数据规模】
Sample Input1
4 5
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80
Sample Output1
60 1
【题目链接】:http://noi.qz5z.com/viewtask.asp?id=u034
【题解】
最小割可以转化为最大流问题;
因为在最大流问题中我们每次在找增广路的时候会把每条边的边权都删掉一个值mi;这个mi是这条道路中边权最小的边的边权;这就说明删掉之后会有一条边的边权变成了0;
而我们在搞最大流的时候会让每条路径都出现一个边权为0的边;且最后找不到可以从s到t的路了;当然这是因为我们在搞最大流的时候弄出来的副产品;因为每条道路都是找最小的边;这正好符合最小割的定义;
输出方案数的话;
把所有的边按照权值升序排;然后按照顺序处理所有的边;
假设删掉这条边;然后做一遍最大流;看看最大流的减少量是不是和这条边的权值一样;如果一样;则这条边是最小割边集;递增答案;并且删掉这条边;然后lastf=新的f;继续重复上述过程;直到lastf为0为止;
如上图;加入删掉2-3这条边;最大流就变成了1;而原来是2;2-1==1所以2-3是最小割边集;
然后我们考虑原来是怎么得出最大流为2的?
必然有一条路径1->2->3->4;然后所有的边都删去2-3这条边的边权;答案递增1;然后再对2->5->6->4这条路径做同样的事情删去5-6这条边的边权;答案递增1;最大流变为2;
删掉之后2-3,5-6都变成了0;而2-3和5-6正是最小割边集;
我们枚举边的思路正在于此;最小割边集删掉之后;最大流的减少量就等于这条边的边权本身;
【完整代码】
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <string>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
using namespace std;
const int MAXN = 40;
const int MAXM = 1e3+10;
const int INF = 2100000000;
const int dx[5] = {0,1,-1,0,0};
const int dy[5] = {0,0,0,-1,1};
const double pi = acos(-1.0);
struct abc
{
int x,y,c;
};
int n,m;
int flow[MAXN][MAXN],tempflow[MAXN][MAXN];
int pre[MAXN];
abc bian[MAXM];
queue <int> dl;
bool mark[MAXN];
void rel(LL &r)
{
r = 0;
char t = getchar();
while (!isdigit(t) && t!='-') t = getchar();
LL sign = 1;
if (t == '-')sign = -1;
while (!isdigit(t)) t = getchar();
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
r = r*sign;
}
void rei(int &r)
{
r = 0;
char t = getchar();
while (!isdigit(t)&&t!='-') t = getchar();
int sign = 1;
if (t == '-')sign = -1;
while (!isdigit(t)) t = getchar();
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
r = r*sign;
}
int get_f()
{
int f= 0;
while (true)
{
memset(mark,false,sizeof(mark));
memset(pre,0,sizeof(pre));
while (!dl.empty())
dl.pop();
mark[1] = true;
dl.push(1);
while (!dl.empty())
{
int x = dl.front();
if (x==n)
break;
dl.pop();
for (int i = 1;i <= n;i++)
if (flow[x][i] && !mark[i])
{
mark[i] = true;
dl.push(i);
pre[i] = x;
}
}
if (!mark[n])
break;
int i = n,mi = INF;
while (i!=1)
{
mi = min(mi,flow[pre[i]][i]);
i = pre[i];
}
i = n;
while (i!=1)
{
flow[pre[i]][i]-=mi;
flow[i][pre[i]]+=mi;
i = pre[i];
}
f += mi;
}
return f;
}
bool cmp(abc a,abc b)
{
return a.c > b.c;
}
int main()
{
// freopen("F:\rush.txt","r",stdin);
rei(n);rei(m);
for (int i = 1;i <= m;i++)
{
rei(bian[i].x);rei(bian[i].y);rei(bian[i].c);
flow[bian[i].x][bian[i].y]+=bian[i].c;
}
for (int j = 1;j <= n;j++)
for (int k = 1;k <= n;k++)
tempflow[j][k] = flow[j][k];
int tempf = get_f(),ans = 0;
for (int j = 1;j <= n;j++)
for (int k = 1;k <= n;k++)
flow[j][k] = tempflow[j][k];
printf("%d ",tempf);
sort(bian+1,bian+1+m,cmp);
for (int i = 1;i <= m;i++)
if (tempf >= bian[i].c)
{
for (int j = 1;j <= n;j++)
for (int k = 1;k <= n;k++)
tempflow[j][k] = flow[j][k];
flow[bian[i].x][bian[i].y]-=bian[i].c;
int temp2f = get_f();
if ((tempf-temp2f)==bian[i].c)
{
ans++;
tempf = temp2f;
tempflow[bian[i].x][bian[i].y]-=bian[i].c;
}
for (int j = 1;j <= n;j++)
for (int k = 1;k <= n;k++)
flow[j][k] = tempflow[j][k];
if (tempf==0)
break;
}
printf("%d
",ans);
return 0;
}