cogs 2047 [ZOJ2676]网络战争 (最小割+01分数规划)

★★★ 输入文件: networkwar.in 输出文件: networkwar.out 评测插件
时间限制:5 s 内存限制:32 MB

【题目描述】

Byteland的网络是由n个服务器和m条光纤组成的,每条光纤连接了两个服务器并且可以双向输送信息。这个网络中有两个特殊的服务器,一个连接到了全球的网络,一个连接到了总统府,它们的编号分别是1和N。

最近一家叫做Max Traffic的公司决定控制几条网络中的光纤,以使他们能够掌握总统府的的上网记录。为了到达这个目的,他们需要使所有从1号服务器到N号服务器的数据都经过至少一条他们所掌握的线路。

为了把这个计划付诸于行动,他们需要从这些线路的拥有者手中购买线路,每条线路都有对应的花费。自从公司的主要业务部是间谍活动而是家用宽带以后,经理就希望尽可能少的花费和尽可能高的回报。因此我们要使购买线路的平均值最小。

如果我们购买了k条线路,花费了c元,我们希望找到使c/k最小的方案。

【输入格式】

多组数据,每组数据第一行是两个整数n和m(1<=n<=100,1<=m<=400),代表服务器的个数和线路数。

之后的m行,每行三个整数a,b,c,分别代表了这条线路所连接的服务器和购买这条线路的花费,花费都是正数且不会超过10^7。

没有自边,没有重边,保证任意两点都是连通的。

最后一行为两个0

【输出格式】

每组数据的第一行是一个整数k,代表购买多少条线路。

之后k个整数,代表购买线路的编号,编号是它们在输入文件被给处的顺序。

每组数据之间有一个空行。

【样例输入】

6 8
1 2 3
1 3 2
2 4 2
2 5 2
3 4 2
3 5 2
5 6 3
4 6 3
4 5
1 2 2
1 3 2
2 3 1
2 4 2
3 4 2
0 0

【样例输出】

4 
3 4 5 6

3
1 2 3



分析:
隐隐约约可以感觉到网络流的气息

先考虑 c [ i ] x [ i ] \sum c[i]*x[i] 的最小值
直接把原图建成网络(流量为花费),1号点为源点,N号点为汇点,直接最小割

而题目要求: c [ i ] x [ i ] x [ i ] \sum c[i]*x[i] \over \sum x[i] 最小值
显然是一个01分数规划的问题

二分一个答案 m i d mid
F ( L ) = c [ i ] x [ i ] L x [ i ] = ( c [ i ] L ) x [ i ] F(L)=\sum c[i]*x[i]-L*\sum x[i]=\sum(c[i]-L)*x[i]
如果 F ( L ) < 0 F(L)<0 ,则说明存在一个小于 L L 的答案

把每条边的边权设为 c [ i ] L c[i]-L ,求最小割即可

####然而这里出现了一个小问题:边权有正有负
我们不是可以解决有正负代价的网络流问题吗?
那是不定流量有正负边权的费用流,我们只选择费用为负的路径增广

那么普通最大流怎么破?

说是最小割,其实这里还是有一点小贪心在里面的(网络流也是一定程度上的贪心)
如果得到的边权为负,我们直接加入F值中
而正边权我们加入网络中,求出最小割加入F值中

因为是双向边,所以反向边的流量等于正向边的流量

#tip
代码大家就看着玩吧
没找到提交的网址。。。逃

好像是special judge

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>

using namespace std;

const double eps=1e-7;
const double INF=1e10;
const int N=110;
const int M=410;
int n,m,st[N],tot=-1,cur[N];
struct node{
	int y,nxt,id;
	double v;
};
node way[M<<1];
int a[N],b[N],deep[N];
double c[N];

void add(int u,int w,double z,int i) {
	tot++;way[tot].y=w;way[tot].v=z;way[tot].id=i;way[tot].nxt=st[u];st[u]=tot;
	tot++;way[tot].y=u;way[tot].v=z;way[tot].id=i;way[tot].nxt=st[w];st[w]=tot;
}

int bfs(int s,int t) {
	queue<int> Q;
    memset(deep,-1,sizeof(deep));
	deep[s]=1;
	Q.push(s);
	while (!Q.empty()) {
		int now=Q.front(); Q.pop();
		for (int i=st[now];i!=-1;i=way[i].nxt)
		    if (way[i].v&&deep[way[i].y]==-1) {
		    	deep[way[i].y]=deep[now]+1;
		    	Q.push(way[i].y);
			}
	}
	return deep[t]!=-1;
}

double dfs(int now,int t,double limit) {
	if (limit<=eps||now==t) return limit;
	double flow=0,f;
	for (int i=st[now];i!=-1;i=way[i].nxt)
	    if (way[i].v&&deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(way[i].v,limit))))
	    {
	    	flow+=f;
	    	limit-=f;
	    	way[i].v-=f;
	    	way[i^1].v+=f;
	    	if (!limit) break;
		}
	return flow;
}

int check(double x) {
	double sum=0;
	memset(st,-1,sizeof(st)); tot=-1;
	
	for (int i=1;i<=m;i++) {
		if (c[i]-x<=eps) sum+=c[i]-x;   //负边直接加入
		else add(a[i],b[i],c[i]-x,i);
	}
	while (bfs(1,n)) 
	    sum+=dfs(1,n,INF);
	return sum<=eps; 
}

int o[N];
bool p[N];

int main()
{
	int cas=0;
	while (scanf("%d%d",&n,&m)!=EOF&&n+m)
	{
		if (cas++) printf("\n");
		
		double l=0,r=0,ans=INF;
		for (int i=1;i<=m;i++) scanf("%d%d%lf",&a[i],&b[i],&c[i]),r=max(r,c[i]);
		
		while (r-l>=eps) {
			double mid=(l+r)/2;
			if (check(mid)) ans=min(ans,mid),r=mid;
			else l=mid;
		}
		
		//printf("%lf\n",ans);
		memset(p,0,sizeof(p));
		for (int i=0;i<tot;i+=2) if (way[i].v<=eps) p[way[i].id]=1;
		int cnt=0;
		for (int i=1;i<=m;i++) {
			if (c[i]-ans<=eps) o[++cnt]=i;
			else if (p[i]) o[++cnt]=i;
 		}
 		printf("%d\n",cnt);
 		for (int i=1;i<=cnt;i++) printf("%d%c",o[i],i==cnt? '\n':' ');
	}
	return 0;
}
发布了941 篇原创文章 · 获赞 192 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/wu_tongtong/article/details/79438227