题意:
有向带权图 找出若干环 每个点只在一个环上 使得权值和最大
思路:
图是很裸的 暴力求出边权
将边权写成矩阵形式 根据题意可知 答案即为每行每列都只选出一个值的最大和
我们将行视为X集合 列视为Y集合 则题意转化为 二分图最佳匹配问题 KM计算
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define oo 99999999
#define M 210
using namespace std;
int n,ans;
int link[M],visx[M],visy[M],val[M][M],slack[M],x[M],y[M],len[M];
char str[M][1010];
int dfs(int now)
{
int i,tmp;
visx[now]=1;
for(i=1;i<=n;i++)
{
if(visy[i]) continue;
tmp=x[now]+y[i]-val[now][i];
if(tmp==0)
{
visy[i]=1;
if(link[i]==-1||dfs(link[i]))
{
link[i]=now;
return 1;
}
}
else if(slack[i]>tmp) slack[i]=tmp;
}
return 0;
}
void KM()
{
int i,j,tmp;
memset(link,-1,sizeof(link));
memset(y,0,sizeof(y));
for(i=1;i<=n;i++)
{
x[i]=-oo;
for(j=1;j<=n;j++)
{
if(x[i]<val[i][j]) x[i]=val[i][j];
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++) slack[j]=oo;
for(;;)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(dfs(i)) break;
tmp=oo;
for(j=1;j<=n;j++)
{
if(!visy[j]&&tmp>slack[j]) tmp=slack[j];
}
for(j=1;j<=n;j++)
{
if(visx[j]) x[j]-=tmp;
}
for(j=1;j<=n;j++)
{
if(visy[j]) y[j]+=tmp;
else slack[j]-=tmp;
}
}
}
for(i=1;i<=n;i++)
{
if(link[i]!=-1) ans+=val[link[i]][i];
}
}
int main()
{
int i,j,k1,k2;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
{
scanf("%s",str[i]);
len[i]=strlen(str[i]);
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(i==j)
{
val[i][j]=0;
continue;
}
for(k1=0,k2=len[j]-1;k1<len[i]&&k2>=0;k1++,k2--)
{
if(str[i][k1]!=str[j][k2]) break;
}
val[i][j]=k1;
}
}
ans=0;
KM();
printf("%d\n",ans);
}
return 0;
}