【题解】游戏

题目描述见Luogu P2462

算法分析

其实这道题并不难,关键是如何转化。因为需要找到最长的单词接龙,就可以用图论来看。单词接龙不会出现环,所以,这就是个DAG上的拓扑排序。如果两个单词可以接在一起,就必须满足以下条件:

  • 前一个单词的字母都必须在后一个单词中出现过
  • 任意一个字母都不能少
  • 后一个单词的长度比前一个单词多1,不能多也不能少

因为没有对顺序作要求,我们只需记录其出现次数即可,并存储它们的哈希值(hash/散列),枚举每个字符串的每个字母,增加其出现次数,并判断该字符串是否存在,如果存在,就建一条有向边。

最后,拓扑排序,记录答案并通过前驱指针递归输出。

代码实现

#include <bits/stdc++.h>
using namespace std;

#if __cplusplus < 201103 || !defined(__cplusplus)
typedef map<int,int> maptype;
#else
typedef unordered_map<int,int> maptype; // 如果是C++11及以上,使用无序哈希映射
#endif

struct edge
{
    int to,nxt;
};

edge e[1000001]; int head[10001],tot;
int in[10001];
maptype mapping;
char str[10001][105];
int len[10001];
int cnt[10001][26];
int f[10001];
int pre[10001];
int n;

void connect(int x,int y){
    e[++tot]=(edge){y,head[x]}; head[x]=tot; ++in[y];
}

int gethash(int idx){ // 哈希函数
    int val=0;
    for(register int i=0;i<26;++i){
        val=val*23+cnt[idx][i];
    }
    return val;
}

void output(int d){ // 递归输出
    if(pre[d]!=0){
        output(pre[d]);
    }
    printf("%s\n",str[d]+1);
}

void topology(){ //拓扑排序
    queue<int> q;
    for(register int i=1;i<=n;++i){
        if(!in[i]) q.push(i);
        f[i]=1;
    }
    while(!q.empty()){
        int x=q.front(); q.pop();
        for(register int i=head[x],y;y=e[i].to,i;i=e[i].nxt){
            if(f[y]<f[x]+1){
                f[y]=f[x]+1;
                pre[y]=x;
            }
            if(--in[y]==0){
                q.push(y);
            }
        }
    }
    int ans=0;
    for(register int i=1;i<=n;++i){
        if(f[ans]<f[i]){
            ans=i;
        }
    }
    printf("%d\n",f[ans]);
    output(ans);
}

void build(){ //建边
    for(register int i=1;i<=n;++i){
        for(register int j=0;j<26;++j){
            ++cnt[i][j];
            int h=gethash(i);
            if(mapping.find(h)!=mapping.end()){ // 如果存在一个可接的单词就建边
                connect(i,mapping[h]);
            }
            --cnt[i][j]; // 要记得还原
        }
    }
}

void input(){
    while(scanf("%s",str[++n]+1)!=EOF); --n; // 注意输入
    for(register int i=1;i<=n;++i){
        len[i]=strlen(str[i]+1);
        for(register int j=1;j<=len[i];++j){
            ++cnt[i][str[i][j]-'a'];
        }
        mapping[gethash(i)]=i;
    }
}

int main(){
    input();
    build();
    topology();
    return 0;
}

原文地址:https://www.cnblogs.com/ctjcalc/p/896656d.html

时间: 2024-10-05 08:00:23

【题解】游戏的相关文章

codevs 1512 转向游戏 题解

Codevs 1512转向游戏 题解 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 白银 Silver 题解 题目描述 Description 小明自认为方向感很好,请小红来测试.小红先让小明面对北方立正站好,然后发出"向左转""向右转"或"向后转"的命令.每个命令执行后,小明都正确地说出了他面对的方向.小红的命令共N个(1≤n≤10000),请你统计小明说[南]的次数. 命令是以数字方式表达: 0---向左转 1---向右转 2

洛谷 P1005 矩阵取数游戏 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1005 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元

洛谷 P1965 转圈游戏 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1965 题目描述 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类推.游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类

Noip2007矩阵取数游戏题解

题目描述 Description [问题描述] 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n?m的矩阵,矩阵中的每个元素ai,j均 为非负整数.游戏规则如下: 每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 每次取走的各个元素只能是该元素所在行的行首或行尾: 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分=被取走的元素值?2i, 其中i表示第i 次取数(从1 开始编号): 游戏结束总得分为m次取数得分之和. 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出

bzoj 1874 取石子游戏 题解 &amp; SG函数初探

[原题] 1874: [BeiJing2009 WinterCamp]取石子游戏 Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 334  Solved: 122 [Submit][Status] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. In

洛谷 P1057 传球游戏 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1057 题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大

题解:luoguP1070 道路游戏(DP)

题目描述 小新正在玩一个简单的电脑游戏. 游戏中有一条环形马路,马路上有 n 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针顺序依次将这 n 个机器人工厂编号为1~n,因为马路是环形的,所以第 n 个机器人工厂和第 1 个机器人工厂是由一段马路连接在一起的.小新将连接机器人工厂的这 n 段马路也编号为 1~n,并规定第 i 段马路连接第 i 个机器人工厂和第 i+1 个机器人工厂(1≤i≤n-1),第 n 段马路连接第 n 个机器人工厂和第 1个机器人

【题解】扑克牌游戏

[题解]扑克牌游戏 假如我们知道一个答案,那么我们剩下的问题就是去检查这个答案是否合法. 显然可以拿\(m\)做文章,假设我们最终可以得到\(ans\)组套牌,那么对于每张牌,如果它的数量比\(ans\)小,我们就需要拿\(joker\)补.多的就没关系了. 很棒的思路,还有一个贪心做法,也很神仙.orz #include<bits/stdc++.h> #define R register int #define gc getchar using namespace std; typedef

题解 滑稽树前做游戏,滑稽树后做交易 trade

题目名称我就不吐槽了... 题目描述 滑稽树前做游戏,滑稽树后做交易 (trade.cpp,3000ms,1024MB) 题目描述 滑稽果被排成一列,poison 的 lsr 要求每个顾客只能买一段连续的区间. sxd 来这里买滑稽果,他对每个滑稽果都有一个喜爱程度 Ai 是一个整数,-100≤Ai≤100, 并保证∑Ai <=2147483647,最终的满意度为所有他买到的滑稽果的喜欢程度之和,如果和 为正(不管是正多少,只要大于 0 即可),则他满意了. 现在 sxd 想知道在他满意的条件下

【题解】数字交换游戏

题目描述 桐桐已经是中学生了.她喜欢研究数字,觉得最漂亮的数就是整数了. 一次,桐桐写下一个整数(无前导0),她想研究下面这个游戏:每次取其中两位交换,会得到一个新的整数——但不能有前导0出现,即第一位不能变成0.这样连续做m次,最后能得到的最大整数是多少? 输入格式 一行,两个整数N(1≤N≤1000000)和m(1≤m≤10). 输出格式 一行,一个整数,为桐桐变化后的最大数,如果不能变换则输出-1. 输入样例一 16375 1 输出样例一 76315 输入样例二 432 1 输出样例二 4