题目
洛谷SP2885 WORDRING - Word Rings
题解
0/1分数规划+spfa判正环
先套0/1分数规划的公式:
再一变
接下来就是一个判正环的问题了。如果有正环则上式>=0,即L有更大取值;否则L无法达到这么优。
提一下建边,真的要做一个什么KMP,AC/后缀自动机,还是马拉车?都是乱来!这样想,它说的是后两个字符,所以不妨直接用两个字符表示点的编号,这样只要给相同字符用一个点,顺便就完成了匹配任务。
总结
我要总结的不是0/1分数规划,是想说一下dfs实现的spfa判正(负)环速率要比bfs实现的spfa要快的多得多。建议以后判环都用dfs来实现spfa。这里的spfa作为一个我dfs实现的spfa的模版。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-4;
const int inf=0x80808080;
const int maxt=26*26,maxn=100010,maxl=1010;
int n;
int ma[maxt+10][maxt+10];
char s[maxl];
struct E{int y,c,next;}e[maxn*2];int len=0,last[maxt+10];
void ins(int x,int y,int c)
{
e[++len]=(E){y,c,last[x]};last[x]=len;
}
double d[maxt+10];bool vis[maxt+10];
bool spfa(int x,double L)
{
vis[x]=true;
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
if(d[y]<d[x]+e[k].c-L)
{
d[y]=d[x]+e[k].c-L;
if(vis[y] || spfa(y,L))
{
vis[x]=false;
return true;
}
}
}
vis[x]=false;
return false;
}
bool check(double L)
{
memset(d,0,sizeof(d));
for(int i=1;i<=maxt;i++)
if(spfa(i,L)) return true;
return false;
}
int main()
{
while(scanf("%d",&n),n)
{
int sum=0;
len=0;memset(last,0,sizeof(last));
memset(ma,0x80,sizeof(ma));
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
int l=strlen(s+1);
if(l<2) continue;sum+=l;
s[1]-='a';s[2]-='a';s[l-1]-='a';s[l]-='a';
int x=s[1]*26+s[2]+1,y=s[l-1]*26+s[l]+1;
ma[x][y]=max(ma[x][y],l);
}
for(int i=1;i<=maxt;i++)
for(int j=1;j<=maxt;j++)
if(ma[i][j]!=inf) ins(i,j,ma[i][j]);
double l=0,r=sum,ans=-1;
while(l-r<=eps)//(l<r)
{
double mid=(l+r)/2;
if(check(mid)) ans=mid,l=mid+eps;
else r=mid-eps;
}
if(ans==-1) puts("No solution.");
else printf("%.2lf\n",ans);
}
return 0;
}