本题的思路主要为01分数规划,把单词的长度作为边的权重,公式推导如下:
把Wi-mid作为新的边权,在图中使用spfa算法,如果找到环说明结果大于mid,此时更新左端点l=mid,反之更新r=mid,直到最后得到结果。
注意:
本题有多组测试数据,所以每次需要置空邻接表和一系列标注数组。
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<iterator>
using namespace std;
struct node{
int w;
int e;
node(int a,int b){
e=a;
w=b;
}
};
const int n=26*26+10;
vector<node> start[n];
int cnt[n];
//注意这里的距离可能不是整数
double dist[n];
int flag[n];
bool check(double mid){
memset(cnt,0,sizeof cnt);
memset(dist,0,sizeof dist);
memset(flag,0,sizeof flag);
queue<int> q;
for(int i=1;i<=26*26;i++){
q.push(i);
flag[i]=1;
}
while(!q.empty()){
int s=q.front();
q.pop();
flag[s]=0;
int count=0;
for(int i=0;i<start[s].size();i++){
node end=start[s][i];
if(dist[end.e]<dist[s]+end.w-mid){
dist[end.e]=dist[s]+end.w-mid;
cnt[end.e]=cnt[s]+1;
count++;
if(count>10000)return 1;
if(cnt[end.e]>n)return 1;
if(!flag[end.e]){
q.push(end.e);
flag[end.e]=1;
}
}
}
}
return 0;
}
int main(){
int N;
cin>>N;
while(N){
for(int i=0;i<N;i++){
string str;
cin>>str;
if(str.length()<2)continue;
string s=str.substr(0,2);
string e=str.substr(str.size()-2,2);
int sn,en;
sn=(s[0]-'a')*26+s[1]-'a';
en=(e[0]-'a')*26+e[1]-'a';
start[sn].push_back(node(en,str.length()));
}
if(!check(0)){
cin>>N;
cout<<"No solution";
continue;
}
double l=0,r=1000;
while(r-l>1e-4){
double mid=(r+l)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%.2lf\n",l);
//每次需要清空邻接表
for(int i=0;i<n;i++){
start[i].clear();
}
cin>>N;
}
return 0;
}