AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1486
最小圈
Description
考虑带权的有向图
G=(V,E)
以及
w:E
–>
R
。
每条边
c=(c[1],c[2],…,c[k])(c[i]∈V)
是
G
中的一个圈当且仅当
这时称
令
μ∗(c)=Min{μ(c)}
为
G
中所有
现在的目标是:在给定了一个图
G=(V,E)
以及
w:E
–>
R
之后,请求出
Input
从文件 input.txt 中读入数据,文件中第一行包含两个整数n和m,并用一个空格隔开,其中
n=|V|,m=|E|
分别表示图中有
n
个点和
Output
输出文件 output.txt 中仅包含一个实数 μ∗(c)=Minμ(c) ,要求输出到小数点后8位。
Sample Input 1
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
Sample Output 1
3.66666667
Sample Input 2
2 2
1 2 -2.9
2 1 -3.1
Sample Output 2
-3.00000000
HINT
样例1中共有2个圈(1,2,3)和(1,2,4)。其中第一个圈的平均值为5,第二个圈的平均值为11/3。样例2中存在一个负圈。
20%的数据:n≤100,m≤1000;
50%的数据:n≤1000,m≤5000;
100%的数据:n≤3000,m≤10000;
100%的数据:|w[i][j]|≤10^7。
Solution
这道题时一道分数规划的题目。先二分答案。
若ans1>ans,则有:
ans1>sumki=1w[c[i]][c[i]+1]/k=>sumki=1(ans1−w[c[i]][c[i]+1])>0
,
即
sumki=1(w[c[i]][c[i]+1]−ans1)<0
,
所以只要将所有边的权值全部减去ans1,若此时图中仍存在负环,那么ans就还可以更小。
若ans1≤ans,同理,
有
sumki=1(w[c[i]][c[i]+1]−ans1)≥0
。
所以如果执行完上面的操作,发现图中不存在负环,那么ans就不能更小了。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int head[3010],nxt[10010],data[10010];
double wei[10010],dis[3010];
bool vis[3010],flag=false;
int n,m,cnt=1;
double l=-10000000,r=10000000;
int num[10010][10];
double ans=10000001;
void add(int x,int y,double z){
nxt[cnt]=head[x];data[cnt]=y;wei[cnt]=z;head[x]=cnt++;
}
void dfs(int now){
vis[now]=true;
for(int i=head[now];i;i=nxt[i]){
if(dis[now]+wei[i]<dis[data[i]]){
if(vis[data[i]]){flag=true;break;}
dis[data[i]]=dis[now]+wei[i];
dfs(data[i]);
}
}
vis[now]=false;
}
bool pan(double now){
memset(head,0,sizeof head);memset(nxt,0,sizeof nxt);
memset(data,0,sizeof data);memset(wei,0,sizeof wei);
cnt=1;
for(int i=1;i<=m;i++)add(num[i][1],num[i][2],num[i][3]-now);
for(int i=1;i<=n;i++){
memset(dis,50,sizeof dis);
flag=false;
dis[i]=0;
dfs(i);
if(flag)return true;
}
return false;
}
int main(){
freopen("cycle.in","r",stdin);
freopen("cycle.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&num[i][1],&num[i][2],&num[i][3]);
while(r-l>=1e-10){
double mid=(r+l)/2;
bool flag=pan(mid);
if(flag){
if(ans>mid)ans=mid;
if(r==mid)break;
r=mid;
}
else{
if(l==mid)break;
l=mid;
}
}
printf("%.8lf",ans);
return 0;
}