您的位置:首页 > 理论基础 > 计算机网络

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

2017-01-15 08:36 519 查看

2047. [ZOJ2676]网络战争

★★★   输入文件:
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

【提示】

在此键入。

【来源】

在此键入。 

题解:最小割+01分数规划

这道题其实就是求割集中的sigma(ci)/|c| ,按照01分数规划的套路,对式子进行变形。

xi∈{0,1},若边在割集中则xi=1,否则xi=0, 最小值R=sigma ci*xi/sigma xi

然后进行二分判定,二分L,求是否满足条件的sigma xi*(ci-L)<=eps 

如何求最小割呢?我们将所有边的边权改为(ci-L),如果边权为负数,那么直接加入答案。否则加边求用最大流求最小割。如果最后的计算结果<=eps那说明L还可以更小

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 4003
#define inf 1000000000
#define eps 1e-7
using namespace std;
int m,n,tot;
int point
,v
,next
,last
,deep
,num
,cur
;
int a
,b
,ck
,mark
,id
;
double c
,remain
;
void add(int x,int y,double z,int k)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; id[tot]=k;
}
double addflow(int s,int t)
{
int now=t; double ans=inf;
while (now!=s) {
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s) {
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
void bfs(int s,int t)
{
for (int i=1;i<=t;i++) deep[i]=t;
queue<int> p; p.push(t); deep[t]=0;
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]==t&&remain[i^1]>eps)
deep[v[i]]=deep[now]+1,p.push(v[i]);
}
}
double isap(int s,int t)
{
double ans=0; int now=s;
bfs(s,t);
for (int i=1;i<=t;i++) num[deep[i]]++;
for (int i=1;i<=t;i++) cur[i]=point[i];
while (deep[s]<t) {
if (now==t) {
ans+=addflow(s,t);
now=s;
}
bool pd=false;
for (int i=cur[now];i!=-1;i=next[i])
if (deep[now]==deep[v[i]]+1&&remain[i]>eps){
last[v[i]]=i;
cur[now]=i;
now=v[i];
pd=true;
break;
}
if (!pd) {
int minn=t;
for (int i=point[now];i!=-1;i=next[i])
if (remain[i]>eps) minn=min(minn,deep[v[i]]);
if (!--num[deep[now]]) break;
num[deep[now]=minn+1]++;
cur[now]=point[now];
if (now!=s) now=v[last[now]^1];
}
}
return ans;
}
bool check(double x)
{
tot=-1;
memset(point,-1,sizeof(point));
memset(num,0,sizeof(num));
memset(ck,-1,sizeof(ck));
double sum=0;
for (int i=1;i<=m;i++)
if (c[i]-x<-eps) sum+=(c[i]-x),ck[i]=1;
else add(a[i],b[i],c[i]-x,i),add(b[i],a[i],c[i]-x,i);
double t=isap(1,n);
sum+=t;
return sum<=eps;
}
int main()
{
freopen("networkwar.in","r",stdin);
freopen("networkwar.out","w",stdout);
while (true) {
scanf("%d%d",&n,&m);
memset(mark,0,sizeof(mark));
if (!n&&!m) break;
double l=0; double r=1e7;
for (int i=1;i<=m;i++) scanf("%d%d%lf",&a[i],&b[i],&c[i]);
double ans=r;
while (r-l>=eps) {
double mid=(l+r)/2;
if (check(mid)) ans=min(ans,mid),r=mid;
else l=mid;
}
int size=0;
for (int i=0;i<=tot;i++)
if (id[i]&&fabs(remain[i])<eps) ck[id[i]]=1;
for (int i=1;i<=m;i++) if (ck[i]==1) size++;
printf("%d\n",size);
for (int i=1;i<=m;i++)
if (ck[i]==1) printf("%d ",i);
printf("\n\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: