NKOI 1852 追查坏奶牛

P1852【USACO4.4.2】Pollutant Control追查坏牛奶
时间限制 : 10000 MS   空间限制 : 65536 KB
问题描述
你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证有三聚氰胺的牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。
输入格式
第一行: 两个整数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) 表示让这辆卡车停止运输的损失。
输出格式
第1行两个整数c、t,c表示最小的损失,T表示要停止的最少卡车数。接下来t 行表示你要停止哪几条线路。如果有多种方案使损失最小,输出停止的线路最少的方案。如果仍然还有相同的方案,请选择开始输入顺序最小的。
样例输入
4 5
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80 
样例输出
60 1 
3

第一个问题求最小割很好解决,求一个最大流就可以了,问题就在于后两个问题
我们首先将每一条边从大到小排序,然后枚举每一条边,如果将这条边删除了并且求出的最大流比原来的最大流减少的部分刚好就等于这条边的权值,我们就将这条边删除,并记下编号继续讨论
最后输出删除的边数和排序后的编号就可以了
#include<cstdio>
#include<iostream>
#include<cstring> 
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
const LL maxn=35,maxm=1005;
const LL inf=99999999999999999LL;
struct wk{LL id,a,b,c;}s[maxm];
LL c1[maxn][maxn],c2[maxn][maxn];
LL n,m,vd[maxn],dis[maxn];
vector<LL>map[maxn];
vector<LL>ans;
bool cmp(wk a,wk b){  
    if(a.c==b.c)return a.id<b.id;  
    return a.c>b.c;  
}  
LL dfs(LL v,LL flow){  
    if(v==n) return flow;  
    LL i,delta=0;  
    for(i=1;i<=n;i++) 
        if(dis[v]==dis[i]+1){  
            LL t=dfs(i,min(c2[v][i],flow-delta));  
            c2[v][i]-=t; 
            c2[i][v]+=t; 
            delta+=t;  
            if(dis[1]==n||delta==flow)return delta;  
        }  
    if(dis[1]==n)return delta;  
    if(--vd[dis[v]]==0)dis[1]=n;  
    vd[++dis[v]]++;  
    return delta;  
} 
void first(){
	for(LL i=1;i<=n;i++)
	    for(LL j=1;j<=n;j++)
	        c2[i][j]=c1[i][j];
}
int main(){
	cin>>n>>m;
	LL i,sum=0;
	for(i=1;i<=m;i++){
		cin>>s[i].a>>s[i].b>>s[i].c;
		s[i].id=i;
		c1[s[i].a][s[i].b]+=s[i].c;
		c2[s[i].a][s[i].b]+=s[i].c;
		map[s[i].a].push_back(s[i].b);
	}
	sort(s+1,s+1+m,cmp);
	while(dis[1]<n)sum+=dfs(1,inf);
	cout<<sum<<" ";
	for(i=1;i<=m;i++){
		first();
		c2[s[i].a][s[i].b]-=s[i].c;
		memset(dis,0,sizeof(dis));
		LL sm=0;
		while(dis[1]<n)sm+=dfs(1,inf);
		if(sm+s[i].c==sum){
			ans.push_back(s[i].id);
			c1[s[i].a][s[i].b]-=s[i].c;
			sum=sm;
		}
	}
	cout<<ans.size()<<endl;
	sort(ans.begin(),ans.end());
	for(i=0;i<ans.size();i++)
	    cout<<ans[i]<<" ";
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值