题意本身比较简单,就是找一个平均值最大的环。
在做这个题之前,我们先解决一个问题,如何找到一个平均值最小的环。
设有一个环中,有k 个点,那么这个环的平均权值X是:
X=(w1+w2+w3+,..,,,wk)/k
然后我们把k乘过去:(w1+w2+…..wk)=kX
Bellman_ford算法的一个重要作用是判断负圈,所以这个题可以利用这个特点来做。
再把(w1+w2+…..wk)=kX处理一下。
就是(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)=0
把这个等号改成小于号:(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)<0
所以我们首先二分这个X,把所有的边权减去X,如果说有平均权值为X的,必定会出现:
(w1-X)+(w2-X)+(w3-X)+(w4-X)+…+(wk-X)=0(w1…wk都是那个环里的点)
所以让这个X稍微大一点点,就会出现负圈了。
由于如果X很大很大的话,也可以产生这个现象。
所以我们要找的X,就是那个刚好使得有这个式子产生的,最小的这个值。
这个值就是 最小的环的平均值(确切的说比平均值稍微大一点点,这里是精度问题了)。
这个题不一样的地方在于,是求最大的,所以我们需要改变一下。
把图中所有的边改成C-w(其中C是一个大于所有w的数字,注意不要溢出)。
再在这个新图里面去找最小的平均值。
就变成了 (C-w1-X)+(C-w2-X)+(C-w3-X)+(C-w4-X)+…+(C-wk-X)=0
然后把这个式子变成了
C-X=(w1+w2.+w3+…+wk)/K
C-X就是新图里最小的平均值。
如果要C-X越小,就意味着X要越大。
所以C-X最小,就意味着X最大。
这个题就这样做出来了。
细节:
①double不可以直接比大小,要写一个eps量,这里我写了1e-3, 连1e-2都会WA的。。
②所有单词,前两个字母所表示的点的dis都是0,然后再跑Bellman-ford。
#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<map>
#include<vector>
using namespace std;
double eps=1e-3;
const double alpha=1001;
bool ashead[120000];
double dis[120000];
map<string,int> mp;
struct Edge
{
int from,to;
double w;
};
vector<Edge> E;
int n;
char input[1200];
void init()
{
memset(ashead,0,sizeof(ashead));
mp.clear();
E.clear();
}
bool Bellman_ford(double mid)
{
for(int i=0;i<120000;i++) dis[i]=1e9;
int size=E.size();
dis[0]=0;
for(int i=0;i<size;i++)
{
if(E[i].from==0)
{
dis[E[i].to]=0;
continue;
}
E[i].w-=mid;
//cout<<E[i].w<<endl;
}
int cnt=mp.size();
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<size;j++)
{
int from=E[j].from;
int to=E[j].to;
double w=E[j].w;
if(dis[to]-(dis[from]+w)>eps)
{
dis[to]=dis[from]+w;
//cout<<dis[to]<<endl;
}
}
}
bool can=true;
for(int j=0;j<size;j++)
{
int from=E[j].from;
int to=E[j].to;
double w=E[j].w;
//cout<<dis[from]<<" "<<dis[to]<<" "<<w<<endl;
if(dis[to]-(dis[from]+w)>eps)
{
//cout<<"fuck"<<endl;
can=false;
}
}
for(int i=0;i<size;i++)
{
if(E[i].from==0) continue;
E[i].w+=mid;
}
return can;
}
int main()
{
while(cin>>n)
{
if(n==0) break;
init();
int cnt=1;
for(int i=1;i<=n;i++)
{
scanf("%s",input);
int len=strlen(input);
if(len<2) continue;//以防万一吧。。。
string temp1;
temp1+=input[0];
temp1+=input[1];
if(mp[temp1]==0) mp[temp1]=cnt++;
string temp2;
//temp.clear();
temp2+=input[len-2];
temp2+=input[len-1];
if(mp[temp2]==0) mp[temp2]=cnt++;
Edge ts;
ts.from=mp[temp1];
ts.to=mp[temp2];
ts.w=alpha-len;
E.push_back(ts);
if(ashead[mp[temp1]]==0)
{
Edge tt;
tt.from=0;
tt.to=mp[temp1];
tt.w=0;
E.push_back(tt);
ashead[mp[temp1]]=1;
}
}
double ans=0x3f3f3f3f;
double l=0;
double r=1200;
//cout<<mp.size()<<endl;
while(r-l>eps)
{
double mid=(l+r)/2;
//cout<<mid<<endl;
if(Bellman_ford(mid)==false)//有负圈
{
ans=min(mid,ans);
r=mid;
}
else l=mid;
}
//cout<<ans<<endl;
if(ans==0x3f3f3f3f) printf("No solution.\n");
else
{
ans=alpha-ans;
printf("%.2lf\n",ans);
}
}
return 0;
}