As Easy as CAB(2016 ICPC Mid-Central USA Region)

本文介绍了一种使用拓扑排序算法解决特定字母排序问题的方法。通过构建有向图并进行拓扑排序,算法能够确定一组字符串中字母的新顺序,避免了矛盾和不确定性,确保结果的正确性和唯一性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

题意:

输入 字母(用变量名 AA 代替) n  

// 分别代表这个串中最大的字母(这个是按照原有字母表的顺序确定的)  、 n个串

按照 新规定字母表的大小顺序 给出由小到大排序好的 n 个字符串,求 a - AA 之间的由小到大的新顺序

 

思路:

每相邻两行进行比较该列字母的大小

遇到当前比较的两个字母不同时,用 ch[i][j] 标记一下,表示第i行与第j行不能继续比较了

用邻接矩阵存图,进行拓扑排序

如果 a---->b && b -----> a同时成立,则输出 "IMPOSSIBLE" ,自相矛盾,根本不可能

如果存在多个入度为0的点,代表结果不唯一,则输出 "AMBIGUOUS"

否则输出唯一确定的答案

例样例1

d 4 
cab 
cda
ccc 
badca

求 a - d 之间字母的新顺序

由第一列可以知 c < b

由第二列知: a < d     a < c     d < c (可以比较的前提是 前一个字母必须相同)

利用拓扑排序画出有向图,根据拓扑排序原理可以得出新顺序是 adcb

CODE :

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define memset(a,n) memset(a,n,sizeof(a))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const LL MAXX = 1e3+10;
string s[MAXX];
int ch[MAXX][MAXX]; // 标记数组
int mp[200][200];  // 存图
int in[100];     // 入度
char ans[100];     // 结果数组

int main()
{
    int n,maxx = -1;
    char l;
    memset(mp,0);
    cin >> l >> n;
    for(int i=0; i<n; i++){
        cin >> s[i];
        int len = s[i].size();
        if(len > maxx)
            maxx = len; // 记录列的最大值
    }

    memset(in,0);
    memset(ch,0);
    int flag =  0;
    for(int j=0; j<maxx; j++){
        for(int i=1; i<n; i++){
            if(ch[i][i-1] == 0){ // 如果第i行可以与第i-1行比较,则继续
                if(s[i][j] != s[i-1][j]){ // 如果当前两个字母不相同,则需要标记ch[][]
                    ch[i][i-1] = 1;
                    if(mp[s[i][j]][s[i-1][j]] == 1){  // 判断矛盾的情况
                        flag = 1;
                        break;
                    }
                    mp[s[i-1][j]][s[i][j]] = 1;  
                }
            }
        }
        if(flag)
            break;
    }
    if(flag){
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }

    // Floyd 
    for(char k='a'; k<=l; k++){
        for(char i='a'; i<=l; i++){
            for(char j='a'; j<=l; j++){
                if(mp[i][k] == 1 && mp[k][j] == 1){
                    if(mp[j][i] == 1){ // 判断矛盾的情况
                        flag = 1;
                        break;
                    } else {
                        mp[i][j] = 1;
                    }
                }
            }
            if(flag)
                break;
        }
        if(flag)
            break;
    }
    if(flag){
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }

   // 统计入度
    for(char i='a'; i<=l; i++){
        for(char j='a'; j<=l; j++){
            if(mp[i][j] == 1 && mp[j][i] == 0 && i!=j)
                in[j]++;
        }
    }
   
   // 判断结果的唯一性
    int cnt = 0;
    while(1){
        int sum = 0;
        char c;
        for(char i='a'; i<=l; i++){
            if(in[i] == 0){
                sum++;
                c = i;
            }
        }
     // 不存在入度为0 的点,存在环,矛盾
        if(sum == 0){
            cout << "IMPOSSIBLE" << endl;
            break;
        }
        // 多个入度为0的点,结果不确定,结果不唯一
        else if(sum > 1){
            cout << "AMBIGUOUS" << endl;
            break;
        }
        ans[cnt++] = c;
        in[c] = -1; // 标记为 -1,区分其他入度为0的点
        if(cnt == l-'a'+1)
            break;
     // 与c相关的点的入度减1
        for(char i='a'; i<=l; i++){
            if(mp[c][i] == 1)
                in[i]--;
        }
    }
    for(int i=0; i<cnt; i++)
        cout << ans[i];
    cout << endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值