您的位置:首页 > 其它

hdu3722Card Game KM算法

2015-08-07 14:53 253 查看
//n个字符串,对于一个字符串i,j 将i颠倒后与j的公共前缀的大小为i,j的权值
//问将这些字符串拼接起来得到的最大权值
//将这些字符串之间建图,直接求完备匹配的最大权匹配
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std ;
const int maxn = 210 ;
const int inf = 0x3f3f3f3f ;
int w[maxn][maxn] ;
char str[maxn][maxn*5] ;
int visx[maxn] , visy[maxn] ;
int match[maxn] , slack[maxn] ;
int n ;int lx[maxn] , ly[maxn] ;
bool find(int x)
{
    visx[x] = 1 ;
    for(int i = 1;i <= n;i++)
    {
        if(visy[i])continue ;
        int tmp = lx[x] + ly[i] - w[x][i] ;
        if(tmp == 0)
        {
            visy[i] = 1 ;
            if(match[i] == -1 || find(match[i]))
            {
                match[i] = x ;
                return true ;
            }
        }
        else slack[i] = min(slack[i] , tmp) ;
    }
    return false ;
}
int KM()
{
   memset(match , -1 , sizeof(match)) ;
   memset(ly , 0 , sizeof(ly)) ;
   for(int i = 1;i <= n;i++)
   {
       lx[i] = -inf ;
       for(int j = 1;j <= n;j++)
         lx[i] = max(lx[i] , w[i][j]) ;
   }
   for(int i = 1;i <= n;i++)
   {
       for(int j = 1;j <= n;j++)
          slack[j] = inf ;
       while(1)
       {
           memset(visx , 0  ,sizeof(visx)) ;
           memset(visy , 0 , sizeof(visy)) ;
           if(find(i))break ;
           int d = inf ;
           for(int j = 1;j <= n;j++)
             if(!visy[j])
               d = min(slack[j] , d) ;
           for(int j = 1;j <= n;j++)
             if(visx[j])
               lx[j] -= d ;
            for(int j = 1;j <= n;j++)
              if(visy[j])
                 ly[j] += d ;
               else
                 slack[j] -= d ;
       }
   }
   int ans = 0 ;
   for(int j = 1;j <= n;j++)
   ans += w[match[j]][j] ;
   return ans ;
}
int main()
{
    //freopen("in.txt" , "r" , stdin) ;
    while(~scanf("%d" , &n))
    {
        for(int i = 1;i <= n;i++)
        {
            scanf("%s" ,&str[i][1]) ;
            for(int j = 1;j < i;j++)
            {
                int len_i = strlen(&str[i][1]) ;
                int len_j = strlen(&str[j][1]) ;
                int len = min(len_i , len_j) ;
                int sum_i = 0 , sum_j = 0 ;
                for(int k = 1;k <= len;k++)
                {
                    if(str[i][len_i  + 1 - k] == str[j][k])
                    sum_i++ ;
                    else break;
                }
                for(int k = 1;k <= len;k++)
                {
                    if(str[i][k] == str[j][len_j + 1 - k])
                    sum_j++ ;
                    else break;
                }
                w[i][j] = sum_i ;
                w[j][i] = sum_j ;
            }
            w[i][i] = 0 ;
        }
        int ans = KM();
        cout<<ans<<endl;
    }
    return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: