UVALive - 6322 The Swapping Game

题意:给一个串以及串中每个字符可以交换的字符是谁,然后一次只可以交换两个字符, 可以交换零次或者多次。求合理的字典序最小的字符串
input:T -〉有多少组数据,初始串str, 接下来n(n = |str|)行,第i行表示第str中第i个字符可以替换成哪几个字符,
output: 如果没有一种合理的解决方法 : NO SOLUTION ,否则输出最小串

思路:二分图匹配, 每一个字符都会给你一个可以交换的序列,想要合法,那就必须使得每一个字符都有可交换的选择(本身也算一种交换)。我们可以字母当做一个节点(编号按字典序顺序,以便求出答案),如果可以置换,建边(单向) ,那么首先我们要检验的就是是否存在一个合理的交换方法,既二分图匹配(详情见代码)。 如果有,那么我们就要求字典序最小的, dfs枚举每一个字符替换成哪一个,然后检查剩下的还能不能成功匹配就好了,第一个成功的置换方法就是答案(因为我们是按顺序dfs)。

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>

using namespace std;
#define MAXN 105
bool g[MAXN][MAXN];
char str[MAXN], s[MAXN];
int match[MAXN], vis[MAXN], f[MAXN], ch[MAXN], cnt;

void init( )
{
    memset(match, -1, sizeof(match));
    memset(vis, 0, sizeof(vis));
    memset(f, 0, sizeof(f));
    memset(ch, 0, sizeof(ch));
}

int dfs( int u, int n)
{
    for( int i = 1; i <= n; i++)
     if(!f[i] && !vis[i] && g[u][i])//f->表示这个节点被没被选中过
     {
         vis[i] = 1;
         if(match[i] == -1 || dfs(match[i], n))
         {
             match[i] = u;
             return 1;
         }
     }
     return 0;
}

int isok(int n)
{
    int i, ans = 0;
    memset(match, -1, sizeof(match));
    for(  i = 1; i <= n; i++)
    {
        if(ch[i]) continue;
        memset(vis, 0, sizeof(vis));
        if(dfs(i, n))
        ans++;
    }
    return ans == cnt;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        memset(g, false, sizeof(g));
        scanf("%s",str +1);

        int len = strlen(str + 1);
        sort(str + 1, str + len + 1);
        for( int i = 1; i <= len; i++)
        {
            scanf("%s",s);
            for( int j = 0; s[j]; j++)
                for( int k = 1; k <= len; k++)
                {
                    if(str[k] == s[j])
                      g[i][k] = 1;
                }
        }

        int ans = 0;
        for(int i = 1; i <= len; i++)
        {
            memset(vis, 0, sizeof(vis));
            if(dfs(i, len)) ans++;
        }
        if(ans < len)
        {
            printf("NO SOLUTION\n");
            continue;
        }
        memset(match, -1, sizeof(match));
        cnt = len;
        for( int i = 1; i <= len ;i ++)
        {
            for( int j = 1; j <= len; j++)
            {
                if(!f[j] && g[i][j])
                {
                    ch[i] = j;
                    f[j] = 1;
                    cnt--;
                    if(isok(len))
                          break;
                    cnt++;
                    f[j] = 0;
                }
            }
        }
        for( int i = 1; i <= len; i++)
            printf("%c",str[ch[i]]);
        puts("");

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值