codejam round1c第二题

前阵子参加了Google的code jam,没有编程功底的人果然过不了第一关,不过事后重新做做还是挺有意思的,例如第一轮第三场的第二题,题目如下:

小明玩个游戏,一开始有N个火车车厢,每个车厢里都有字母,现在要组装火车,要求把所有车厢连在一起组成字符串,这个字符串要求相同的字母只能相邻,问现在有几种组装的方式。例如有三个车厢a,a,ab,组装的方式就有123,213两种。

题目讲完,现在来分析题目,先来考虑一个简单的情况,各个车厢的字母都是不一样的,相互独立,那么这时可拼装的方式一共有N!种,这也是题目最大的组装方式数。那么,我们需要把所有车厢的状态转化成上述独立状态,为了达到以上要求,需要首先处理每个车厢连续重复的字母,方便统计,然后处理所有单个字母,把所有相同的单个字母合并在一起,此时合并后的车厢内部可交换方案数目为字母数的阶乘,接着把单个字母和其他有相同结尾或开头的多个字母合并,合并后的车厢内部可交换方案数目为两个车厢可交换数的乘积,这样一来,原来单个字母的车厢要么已经合并,要么就跟其他所有车厢的字母都不一样,处理完单个字母,最后再把多个字母的车厢合并,合并后的车厢内部可交换数目为合并前两个车厢可交换数的乘积。上述处理结束后,假设还剩M个车厢,各个车厢的内部可交换数为A1,A2,...,Am,那么最后火车组装的方式一共有M!*A1*A2*...*Am,需要注意的是,处理车厢前需要检查各个车厢是否满足相同字母只能相邻的条件,然后处理车厢结束后,检查拼在一起的一种方案中组成的字符串是否满足相同字母只能相邻的条件。

解题思路就讲到这里,下面就直接上代码吧。

首先车厢的数据结构,包括车厢字符串,字符串里单个字母的数量,内部可交换的数目,以及一个表示车厢是否被合并过的,是否有效的布尔型。

struct train{
    bool isValid;
    int oneNum;
    unsigned long long exchange;
    string name;
    train():isValid(true),oneNum(0),exchange(1){};
};

然后就是合并单个字母函数

void mergeOneLetter(train* trainset,int len){
    for (int i=0; i<len; i++) {
        train& currentTrain=trainset[i];
        if (currentTrain.isValid&¤tTrain.name.length()==1) {
            //先把所有单个字符合并
            for (int j=i+1; j<len; j++) {
                train& otherTrain=trainset[j];
                if (otherTrain.isValid&&otherTrain.name.length()==1&&otherTrain.name[0]==currentTrain.name[0]) {
                    currentTrain.oneNum++;
                    currentTrain.exchange*=currentTrain.oneNum;
                    otherTrain.isValid=false;
                }
            }
            //再合并多个字符的,确保单个字符要么不能连,要么已经合并不再是单个字符,方便下一步
            for (int j=0; j!=i&&j<len; j++) {
                train& otherTrain=trainset[j];
                if (otherTrain.isValid) {
                    if (otherTrain.name[0]==currentTrain.name[0]) {
                        otherTrain.isValid=false;
                        stringstream mergeStream;
                        mergeStream<<currentTrain.name<<otherTrain.name;
                        currentTrain.name=mergeStream.str();
                        currentTrain.exchange*=otherTrain.exchange;
                        break;
                    }
                    if (otherTrain.name[otherTrain.name.length()-1]==currentTrain.name[0]) {
                        otherTrain.isValid=false;
                        stringstream mergeStream;
                        mergeStream<<otherTrain.name<<currentTrain.name;
                        currentTrain.name=mergeStream.str();
                        currentTrain.exchange*=otherTrain.exchange;
                        break;
                    }
                }
            }
        }
    }
}

接着是合并其他车厢的函数

//合并所有火车
void mergeAllTrain(train* trainset,int len){
    for (int i=0; i<len; i++) {
        train& currentTrain=trainset[i];
        if (currentTrain.isValid) {
            currentTrain.isValid=false;
            char firstChar=currentTrain.name[0],lastChar=currentTrain.name[currentTrain.name.length()-1];
            int firstOccur=-1,lastOccur=-1;
            while ((firstOccur=findFirstLetter(trainset, len, lastChar))!=-1) {
                train& headTrain=trainset[firstOccur];
                headTrain.isValid=false;
                stringstream mergeStream;
                mergeStream<<currentTrain.name<<headTrain.name;
                currentTrain.name=mergeStream.str();
                currentTrain.exchange*=headTrain.exchange;
                lastChar=currentTrain.name[currentTrain.name.length()-1];
            }
            while ((lastOccur=findLastLetter(trainset, len, firstChar))!=-1) {
                train& tailTrain=trainset[lastOccur];
                tailTrain.isValid=false;
                stringstream mergeStream;
                mergeStream<<tailTrain.name<<currentTrain.name;
                currentTrain.name=mergeStream.str();
                currentTrain.exchange*=tailTrain.exchange;
                firstChar=currentTrain.name[0];
            }
            currentTrain.isValid=true;
        }
    }
}

附上判断一个车厢是否满足相同字母只能相邻的代码,判断整列火车方法也类似。

//判断一个train是否有效
bool judgeOneTrain(const train& theTrain){
    set<char> occurSet;
    string testString=theTrain.name;
    occurSet.insert(testString[0]);
    for (int i=1; i<testString.length(); i++) {
        char letter=testString[i];
        if(letter!=testString[i-1]){
            if (occurSet.find(letter)!=occurSet.end()) {
                return false;
            }
            else{
                occurSet.insert(letter);
            }
        }
    }
    return true;
}

最后,这道题我只过了小数据,因为要过大数据,涉及大数阶乘,要用数组辅助,我也不想做了。

codejam round1c第二题

时间: 2024-11-01 13:47:41

codejam round1c第二题的相关文章

2016/1/12 第一题 输出 i 出现次数 第二题 用for循环和if条件句去除字符串中空格 第三题不用endwith 实现尾端字符查询

1 import java.util.Scanner; 2 3 4 public class Number { 5 6 private static Object i; 7 8 /* 9 *第一题 mingrikejijavabu中字符“i” 出现了几次,并将结果输出*/ 10 public static void main(String[] args) { 11 12 String r ="imingrikejijavabi"; 13 14 15 //第一种 截取 16 int a=

最后一周第二天训练赛之第二题

试题: B - B Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice SPOJ ICODER Description Mathews uses a brand new 16-bit instruction processor. (Yeah i am being sarcastic!). It has one register (say R) and it su

Learning Perl 第九章习题第二题

把输入文件中的所有Fred换成Larry, 不区分大小写. 知识点 1. 文本文件读写 2. 简单的正则替换 3. unless 的用法 4. $_ 的用法 Learning Perl 第九章习题第二题,布布扣,bubuko.com

2014百度之星资格赛第二题

Disk Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2560    Accepted Submission(s): 366 Problem Description 有很多从磁盘读取数据的需求,包括顺序读取.随机读取.为了提高效率,需要人为安排磁盘读取.然而,在现实中,这种做法很复杂.我们考虑一个相对简单的场景.

NOIP2005-普及组复赛-第二题-校门外的树

题目描述 Description 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L,都种有一棵树. 由于马路上有一些区域要用来建地铁.这些区域用它们在数轴上的起始点和终止点表示.已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分.现在要把这些区域中的树(包括区域端点处的两棵树)移走.你的任务是计算将这些树都移走后,马路上还有多少棵树. 输入输出

第二题、第三题、第四题

1.以编程方式操作 HttpCachePolicy 类. HttpCachePolicy.SetExpires HttpCachePolicy.SetCacheability |NoCache|Private|Public|Server|ServerAndNoCache |ServerAndPrivate 2<%@ OutputCache Duration="60" VaryByParam="None" %>Duration 和 VaryByParam

05:统计单词数【NOIP2011复赛普及组第二题】

05:统计单词数 总时间限制:  1000ms 内存限制:  65536kB 描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给定的文章中出现的次数和第一次出现的位置.注意:匹配单词时,不区分大小写,但要求完全匹配,即给定单词必须与文章中的某一独立单词在不区分大小写的情况下完全相同(参见样例1),如果给定单词仅是文章中某一单词的一部分则不算匹配(参见样

LeetCode 第二题,Median of Two Sorted Arrays

题目再现 There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). 题意解析 题目意思是给两个大小为m,n的有序数组(m,n可能为0),要求找出这两个数组的中位数.并且程序的时间复杂度必须不能超过O(log(m+n)). 这道题的

“金山杯2007逆向分析挑战赛”第一阶段第二题

注:题目来自于以下链接地址: http://www.pediy.com/kssd/ 目录:第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第二题 \ 题目 \ [第一阶段 第二题] 题目描述: 己知是一个 PE 格式 EXE 文件,其三个(section)区块的数据文件依次如下:(详见附件)  _text,_rdata,_data 1. 将 _text, _rdata, _data合并成一个 EXE 文件,重建一个 PE 头,一些关键参数,如 EntryPoint,Imp