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
分析:
隐隐约约可以感觉到网络流的气息
先考虑
的最小值
直接把原图建成网络(流量为花费),1号点为源点,N号点为汇点,直接最小割
而题目要求:
最小值
显然是一个01分数规划的问题
二分一个答案
如果
,则说明存在一个小于
的答案
把每条边的边权设为 ,求最小割即可
####然而这里出现了一个小问题:边权有正有负
我们不是可以解决有正负代价的网络流问题吗?
那是不定流量有正负边权的费用流,我们只选择费用为负的路径增广
那么普通最大流怎么破?
说是最小割,其实这里还是有一点小贪心在里面的(网络流也是一定程度上的贪心)
如果得到的边权为负,我们直接加入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;
}