pku2949——分数规划/图论
题目大意:给定一堆字符串,如果第一个字符串的后两个字母和第二个字符串的前两个字母相同,那么可以把他们俩接起来,求平均长度最大的一个环。
看到题目第一反映就是——这是个图论问题。建图也比较好想一点,做过一些欧拉回路问题的同学肯定都会。关键是求最大平均值,需要利用分数规划。
关于分数规划的介绍请看2007年胡伯涛的OI论文(1.6小节)。
在这个问题里面我们需要最大化目标函数 Max{r | r = sigma(w[i])/len}(len为环的长度,w[i]为边权),我们构造函数g(r) = Max{sigma(w[i])-r*|w|} = Max{w[1]-r+w[2]-r+...+w[len]-r}。也就是说计算g(r)等价于:先将所有的边权减去r,然后在图中找一个最大长度的环。我们并不需要知道g(r)的准确值,只需要知道它与0的大小关系。假设g(r)大于0,则图中一定存在正环~否则的话g(r)<=0。根据g(r)关于r的单调性(随r的增加递减),可以二分r来求零点。
这个题我不知道提交了多少次。。。。悲剧啊,最多有26*26+26个点,我写了个26*26,忘记加26了。。。。我怀疑我今晚26了。。。
View Code
#include <iostream>
#include <cstdio>
#include <cstring>
usingnamespace std;
constint N =210000;
constint M =1010;
constdouble eps = 1e-2;
constdouble inf = 1e9;
int n, m, tot, h[M], queue[N], v[N], nxt[N];
double w[N], tmp[N], dis[M];
char str[M];
bool visit[M];
void add(int a, int b, double c)
{
v[tot] = b;
tmp[tot] = c;
nxt[tot] = h[a];
h[a] = tot++;
}
bool spfa(int u)
{
int i;
bool flag =false;
visit[u] =true;
for(i = h[u]; i !=-1; i = nxt[i])
{
if(dis[v[i]] < dis[u]+w[i])
{
dis[v[i]] = dis[u] + w[i];
if(!visit[v[i]]) flag = spfa(v[i]);
else flag =true;
if(flag) returntrue;
}
}
visit[u] =false;
returnfalse;
}
bool check()
{
int i, j;
for(i =1; i <= n; i++)
{
memset(visit, false, sizeof(visit));
for(j =1; j <= n; j++) dis[j] =0.0;
if(spfa(i)) returntrue;
}
returnfalse;
}
int main()
{
int i, len, u, v;
double l, r, mid;
while(scanf("%d", &m) != EOF)
{
if(m ==0) break;
memset(h, -1, sizeof(h));
tot =0;
r =0.0;
for(i =0; i < m; i++)
{
scanf(" %s", str);
len = strlen(str);
r = max(r, (double)len);
u = (str[0]-'a'+1)*26+str[1]-'a'+1;
v = (str[len-2]-'a'+1)*26+str[len-1]-'a'+1;
add(u, v, (double)len);
}
n =26*26+26;
l =0.0;
r +=1.0;
while(r-l >= eps)
{
mid = (l+r) /2;
for(i =0; i < tot; i++) w[i] = tmp[i]-mid;
if(check())
l = mid;
else r = mid;
}
if(l <0.01) printf("No solution.\n");
else printf("%.2f\n", l);
}
return0;
}