COCI2016/2017 Round1T3 Cezar

文章目录

题目

题目1
题目2

分析

先把单词按给定的顺序排好。

然后依次比相邻的两个单词,只要不是包含关系,一定能且只能得到一对字母的大小关系,把大的字母向小的字母连一条边。
例如:abcadd,要求替换后abc<add
第一位a是一样的,不论怎么替换都没用,跳过。
第二位一个是b一个是d,由于要求替换后abc<add,所以必须保证替换后的b<d
这时把db连一条边。

这样操作后能得到一个图。
显然如果图中有环,输出NE

否则,我们就会得到一个图,表示的是替换后要满足的字母大小关系。
这个图虽然不是树(因为可能有下面这种情况),但是有一个根(无入度的点)。
图1
其实边E是多余的,如果能把这样的边都删掉就简单了,但是要删掉这种多余的边很麻烦。

所以我在考试的时候使用了一种玄学方法,创新驱动发展 找到“根”(如图中的b),用SPFA跑一遍从根开始的最长路(还可以顺便判环),得到根与每个点 i i i的最长距离 d i d_i di,将 d d d从大到小排序后,依次给 d i d_i di路径上的点编号,这样就能避免上图中E这样的边了。

就这样,我得了0分。
最后发现是DA打成了DE,又是绑点数据,我丢,,,,,,

然而改正后还是只有80分,发现忽略了一个细节问题,当建出来的图没有边(即每个单词都是后一个的前缀)时,是有解的,我的代码太丑了,没考虑到这种问题。
所以虽然下面给的代码是AC的,但是大家最好别看。

PS.这道题本来是想考拓扑排序,,,,,,

代码

真的不建议看!!!

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

#define MAXN 100
#define MAXL 100
int N;
struct node{
    int len;
    char str[MAXL+5];
}A[MAXN+5];
int P[MAXN+5];
char S[MAXN+5][MAXL+5];

int F[30];
vector<int> G[30];
char Ans[30];

bool vis[30];
int dist[30],pre[30],cnt[30];
bool SPFA(int S,int n){
    queue<int> Q;
    Q.push(S);
    memset(dist,0x3f,sizeof dist);
    dist[S]=0;
    pre[S]=-1;
    vis[S]=cnt[S]=1;
    while(!Q.empty()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=0;i<int(G[u].size());i++){
            int v=G[u][i];
            if(dist[u]-1<dist[v]){//每条边取负求最短路 得到最长路
                dist[v]=dist[u]-1;
                pre[v]=u;
                if(!vis[v]){
                    vis[v]=1;
                    cnt[v]++;
                    if(cnt[v]>n)//判环(负权回路)
                        return 0;
                    Q.push(v);
                }
            }
        }
    }
    return 1;
}

struct path{
    int len,To;
}T[30];
bool cmp(path x,path y){
    return x.len>y.len;
}

char C='z';
void Update(int u){
    if(Ans[u])
        return;
    Update(pre[u]);
    Ans[u]=C--;
}

int main(){
    freopen("cezar.in" ,"r", stdin);
    freopen("cezar.out","w",stdout);
    N=read();
    for(int i=1;i<=N;i++)
        scanf("%s",S[i]+1);
    for(int i=1;i<=N;i++){
        P[i]=read();
        A[i].len=strlen(S[P[i]]+1);
        memcpy(A[i].str,S[P[i]],sizeof S[P[i]]);
    }//按给定顺序排
    bool flag=1;
    for(int i=2;i<=N;i++){
        int j=1;
        while(A[i-1].str[j]==A[i].str[j])
            j++;//找到第一个不同的字母
        if(j==A[i-1].len+1)
            continue;//前缀
        flag=0;
        if(j==A[i].len+1){
            puts("NE");
            return 0;
        }
        G[A[i].str[j]-'a'+1].push_back(A[i-1].str[j]-'a'+1);
        F[A[i-1].str[j]-'a'+1]=A[i].str[j]-'a'+1;
    }//建图,建议字母编号从1开始,不然后面有点麻烦
    if(flag){
        puts("DA");
        for(int i=0;i<26;i++)
            putchar('a'+i);
        return 0;
    }//这玩意就是处理每个都是后一个的前缀的问题
    int Root=0,cnt=0;
    for(int i=1;i<=26;i++){
        if(G[i].size()&&!F[i])
            Root=i;
        cnt+=F[i];
    }//找根
    if(!Root){
        puts("NE");
        return 0;
    }
    for(int i=1;i<=26;i++)
        if(i!=Root&&!F[i])
            G[Root].push_back(i);
    if(!SPFA(Root,cnt+1)){
        puts("NE");
        return 0;
    }
    Ans[Root]=C--;
    for(int i=1;i<=26;i++){
        T[i].To=i;
        T[i].len=dist[i];
    }
    sort(T+1,T+26+1,cmp);
    for(int i=1;i<=26;i++){
        int tmp=T[i].To;
        Update(tmp);//递归找路径编号
    }
    puts("DA"),puts(Ans+1);
}

PS.据说用拓扑排序60行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值