/*
枚举每一个平均长度mid值。
如果存在一个环,(E1+...+Ek)/k>=mid(其中k是边数,E1……Ek是各个边权),
那么正解比mid大,否则比mid小,这就是二分策略。
那么怎样知道是否存在(E1+...+Ek)/k>=mid 呢?
如下转化:(E1+...+Ek)>=mid*k
E1-mid + E2–mid + E3-mid + ... + Ek-mid >= 0
所以,把所有的边权改为Ei – mid,然后看是否存在正环就可以,存在就是满足条件。
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int S=0; //图非连通,建超级源点变连通,不改变环的存在与否
const int NN=700;
const int MM=5000000;
const double inf=10000000000.0;
const double eps=1e-4; //精度为输出要求的百分之一即可,若涉及乘法,则要万分之一
struct Edge{
int v;
double dis;
int next;
}edge[MM];
int n,ecnt,sum;
int map[NN][NN];
int head[NN];
bool hash[NN];
char zf[1005];
void addedge(int u,int v,double dis)
{
edge[ecnt].v=v;
edge[ecnt].dis=dis;
edge[ecnt].next=head[u];
head[u]=ecnt++;
}
void build() //建图
{
ecnt=0;
memset(head,-1,sizeof(head));
int n=26*26;
sum=0;
for (int i=1; i<=n; i++)
{
if (hash[i])
{
for (int j=1; j<=n; j++)
{
if (map[i][j]) addedge(i,j,map[i][j]*1.0);
}
addedge(S,i,0.0);
sum++; //统计有效点
}
}
}
int inq[NN];
int cou[NN];
int q[NN],front,rear;
double d[NN];
bool spfa(double ave) //bfs版spfa判环,听说dfs版判环比较快,写了几遍都是TLE。。。
{
memset(inq,false,sizeof(inq));
memset(cou,0,sizeof(cou));
for (int i=0; i<NN; i++) d[i]=inf;
d[S]=0.0;
q[front=0]=S;
rear=1;
while (front!=rear)
{
int u=q[front];
front=(front+1)%NN;
inq[u]=false;
for (int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if (d[v]>d[u]+ave-edge[i].dis) //这里转化为判负环
{
d[v]=d[u]+ave-edge[i].dis;
if (!inq[v])
{
inq[v]=true;
if (d[v]<d[q[front]]) //LLL优化后比stack版又快了一点点,到719ms了
{
front=(front-1+NN)%NN;
q[front]=v;
}
else
{
q[rear]=v;
rear=(rear+1)%NN;
}
}
}
if (++cou[v]>sum) return true;
}
}
return false;
}
void solve(int mm)
{
double l=0.0;
double r=mm*1.0;
double mid;
while (r-l>=eps) //二分平均数
{
mid=(l+r)/2;
if (spfa(mid)) l=mid;
else r=mid;
}
if (mid<1.0) printf("No solution.\n");
else printf("%.2f\n",mid); //听说写成%.2lf的G++能过,C++过不了
}
int main()
{
while (scanf("%d",&n)!=EOF && n)
{
getchar();
memset(map,0,sizeof(map)); //为了去重边
memset(hash,false,sizeof(hash)); //为了统计点数sum和一毛钱的优化
int mm=0;
for (int i=1; i<=n; i++)
{
gets(zf);
int len=strlen(zf);
if (len<=1) continue;
if (len>mm) mm=len;
int x=(zf[0]-'a')*26+zf[1]-'a'+1;
int y=(zf[len-2]-'a')*26+zf[len-1]-'a'+1; //转码,起始为1,0做超级源点用
if (len>map[x][y]) map[x][y]=len; //去重边
hash[x]=true;
hash[y]=true;
}
build();
solve(mm);
}
return 0;
}
POJ2949-平均最短路SPFA
最新推荐文章于 2021-06-22 11:02:01 发布