[USACO]1.2.3 name that number

原创 2016年08月30日 02:48:46

题目大意

:输入一串数字,根据数字可以导出一大堆名字,要找出所有既能导出,又存在于字典当中的名字。其中2可以代表A,B,C;3代表D,E,F,依此类推,但是字母Q和Z不存在。
键盘分布
其中字典需要单独读入,文件名为dict.txt

首先是正常人的解法

不要被题目骗了
题目叫你通过数字找出所有名字你就通过数字找出所有名字???
那你484撒(对我就是撒

深搜的深度最大为13,需要遍历3^d个搜索树节点。

改变一下策略,反正你要挨个读入字典dict.txt,那还不如反着把字典里头的名字转化成数字代码。
打表:

int map[26]={2,2,2,//ABC
            3,3,3,//DEF
            4,4,4,//GHI
            5,5,5,//JKL
            6,6,6,//MNO
            7,0,7,7,//PQRS,其中Q不存在所以设为0
            8,8,8,9,9,9};

论如何将一个“名字”转化成代码

int transfer(string name){
    int ret=0;
    for(int i=0;i<name.length();i++){
        ret*=10;
        ret+=map[name[i]-'A'];//数据保证大写
    }
    return ret;
}
//main()
FILE *d=fopen("dict.txt","r");
char buffer[30];
while(~fscanf(d,"%s",buffer){
    if(transfer(buffer)==code){
        exist=true;
        cout<<buffer<<endl;
    }
}
if(!exist)cout<<"NONE";

做一个逗逼

我就脑残地建立了两棵字典树+dfs(微笑)虽然时间慢了(每次都要把所有情况算出来)而且空间大了(这不明显么)然而还是过了13组数据(共15组,以wa告终,我错在没有排除Q和Z)我觉得那行吧顺便复习下字典树和dfs,就又写了一遍。(逗逼青年欢乐多)


字典树:

struct node {
    node *child[26];
    bool exist;//记录是否为终结点
    //终结点是我自己起的名字,我并不知道应该叫什么= =
    node() {
        for (int i = 0; i < 26; i++)child[i] = 0;
        exist = 0;
    }
};
class Trie {
    node *root;
public:
    Trie() {
        root = new node;
    }
    void insert(string str) {
        node *scout = root;
        for (int i = 0; i < str.length(); i++) {
            int target = str[i] - 'A';//题目保证全大写
            if (!scout->child[target])scout->child[target] = new node;
            scout = scout->child[target];
        }
        scout->exist = 1;
    }
    friend void compare(Trie&, Trie&);
};

为了题目,我暂时没有写查询函数,因为不需要(废话)
其中compare(Trie &,Trie &) 是用来同时遍历两棵字典树用的。规则如下:

1、对于任意结点,如果a树中存在但b树中不存在,则不访问此节点
2、对于任意节点,如果此节点同时在a、b两棵树中以终结点(从根结点到此节点能够形成一个存在字典中的单词)则输出此节点的单词。

我的trie树节点的特点是,结点本身不存储自己是’a’还是’b’,链接到上一节点的对应位置,这样既可以保证访问速度又可以保证存储空间。

对任意树遍历方法:

void traversal(node *target, string current) {
    for (int i = 0; i < 26; i++)
        if(target->child[i])
            traversal(target->child[i],current+(char)(i+'a'));
    if (target->exist){
        //todo:输出节点信息
        //optional:计数器++
    }
}
/*调用:*/traversal(root,"");

言归正传,现在用这个开始做题
首先实例化两棵字典树all和dict

Trie all,dict;

咱先把那个神奇的数字串dfs一下,把所有可能的名字找出来(逗逼本质暴露的时刻到了)

string key;
void dfsName(int digit, int num, string name) {
    if (digit >= key.length()) {
        all.insert(name);//向其中一棵字典树中插入能打出的所有名字
        return;
    }
    for (int i = 0; i < 3; i++) {
        dfsName(digit + 1, i, name + (char)(i + (key[digit] - '0') * 3 - 6 + 'A'));
        //错就错在这了,这里只适用于'A'到'P'之间的名字。
        //总之我懒得改了
    }
}
//把电话号码读到key里头然后调用:
dfsName(0,0,"");

然后再读取dict.txt…

FILE *dicttxt=fopen("dict.txt","r");
char buffer[30];//13
while(~fscanf(dicttxt,"%s",buffer))dict.insert(buffer);

至此我们有了两棵字典树。接下来我们要做的就是把两颗字典树“重叠”起来,同时遍历,就可以找到所有共有的终结点了。

void compare(node *a, node *b, string current) {
    node *sca = a, *scb = b;
    for (int i = 0; i < 26; i++) {
        if (sca->child[i] && scb->child[i])
            compare(sca->child[i], scb->child[i], current + (char)(i + 'A'));
            //根据遍历策略,我需要走一步,"顺"一个字母。从上到下连起来成为单词
    }
    if (sca->exist&&scb->exist){
            good=true;
            cout << current.c_str() << endl;
        }
}
void compare(Trie &a, Trie &b) {
    compare(a.root, b.root, "");
    //这就是为什么我在trie类中要声明friend void compare (Trie &,Trie &);
}

综上,在这里放一下我ac的代码(成就:放肆的逗逼青年:达成)

#include<iostream>
using namespace std;
struct node {
    node *child[26];
    bool exist;
    node() {
        for (int i = 0; i < 26; i++)child[i] = 0;
        exist = 0;
    }
};
class Trie {
    node *root;
public:
    Trie() {
        root = new node;
    }
    void insert(string str) {
        node *scout = root;
        for (int i = 0; i < str.length(); i++) {
            int target = str[i] - 'A';
            if (!scout->child[target])scout->child[target] = new node;
            scout = scout->child[target];
        }
        scout->exist = 1;
    }
    friend void compare(Trie&, Trie&);
}all, dict;
bool good=0;
void compare(node *a, node *b, string current) {
    node *sca = a, *scb = b;
    for (int i = 0; i < 26; i++) {
        if (sca->child[i] && scb->child[i])
            compare(sca->child[i], scb->child[i], current + (char)(i + 'A'));
    }
    if (sca->exist&&scb->exist){
            good=true;
            cout << current.c_str() << endl;
        }
}
void compare(Trie &a, Trie &b) {
    compare(a.root, b.root, "");
}
string key;
void dfsName(int digit, int num, string name) {
    if (digit >= key.length()) {
        all.insert(name);
        return;
    }
    for (int i = 0; i < 3; i++) {
        char newDigit=(char)(i + (key[digit] - '0') * 3 - 6 + 'A');
        if(newDigit>='Q')newDigit++;
        dfsName(digit + 1, i, name + newDigit);
    }
}
int main() {
    freopen("namenum.in","r",stdin);
    freopen("namenum.out","w",stdout);
    char a[13];
    FILE *diccc=fopen("dict.txt","r");
    while(~fscanf(diccc,"%s",a))dict.insert(a);
    cin >> a;
    key = a;
    dfsName(0, 0, "");
    compare(all, dict);
    if(!good)cout<<"NONE";
}

好的。我觉得没有人会像我这样写这样逗逼的题解。所以我决定继续写下去。

其实,USACO是支持C++11的,也就是说我可以放肆地用STL(standard template library)里的set,不用担心兼容不兼容。所以其产物如下。

#include<set>
using namespace std;
#include<iostream>
set<string> dict;
bool exist=false;
string key;
void dfsName(int digit, int num, string name) {
    if (digit >= key.length()) {
        if(dict.find(name)!=dict.end()){
            exist=true;
            cout<<name<<endl;
        }
        return;
    }
    for (int i = 0; i < 3; i++) {
char newDigit=(char)(i + (key[digit] - '0') * 3 - 6 + 'A');
        if(newDigit>='Q')newDigit++;
        dfsName(digit + 1, i, name+newDigit);}
}
int main(){
    freopen("namenum.in","r",stdin);
    freopen("namenum.out","w",stdout);
    FILE *diccc=fopen("dict.txt","r");
    while(~fscanf(diccc,"%s",a))dict.insert(a);    
    cin>>a;key=a;
    dfsName(0,0,"");
    if(!exist)cout<<"NONE";
}

ac
interesting

版权声明:本文为博主原创文章,爱转转不转算

usaco 1.2.3 name that number

我一定是和文件有仇是吧一定是的。。下一道题回文那个难得做的比较顺(只是比较。。囧- -)而且本机上测得没什么问题,以为一定能一次ac了,结果输出文件的stdout手贱写成了stdin。。。。不过至少是...
  • shshwdr
  • shshwdr
  • 2012年02月13日 21:16
  • 479

USACO section 1.2.3 Name That Number

1. 比较简单,但是用到了文件读写,终于明白了给的test代码中的fout和fin是什么意思了,哈哈; 2. 如果按照一般的思路,肯定会超时,所以把dict转换成数字,这样的算法效率是固定的 ...

Usaco 1.2.3 命名那个数字(Name That Number)

题目:  命名那个数字 来源:  Usaco1.2.3 题目大意:  给定一个数字,求他所组成的符合规定的字符串 数据范围:  数字长度1~12 样例:...

【USACO题库】1.2.3 Name That Number命名那个数字

题目描述 在威斯康辛州牛大农场经营者之中,都习惯于请会计部门用连续数字给母牛打上烙印。 但是,母牛用手机时并没感到这个系统的便利,它们更喜欢用它们喜欢的名字来呼叫它们的同伴,而不是用像这个的语句"...
  • gmh77
  • gmh77
  • 2016年08月15日 16:29
  • 194

[USACO 1.2.3] Name That Number

[题目描述] Name That Number 命名那个数字 在威斯康辛州牛大农场经营者之中,都习惯于请会计部门用连续数字给母牛打上烙印。 但是,母牛用手机时并没感到这个系统的便利,它们更...

USACO答案name that number

  • 2013年12月12日 16:46
  • 787B
  • 下载

1.2.3 Name That Number

思路:  1 首先读取字典里的字符串到字符串数组中, 并排序;排序目的是减少查找时间                       2: 按照输入的数字用DFS产生所有的字符串, 并判断是否在字典中;...

usaco Name That Number

 Name That Number命名那个数字译 by timgreen在威斯康辛州牛大农场经营者之中,都习惯于请会计部门用连续数字给母牛打上烙印。但是,母牛用手机时并没感到这个系统的便利,它们更喜欢...

USACO 1.2 - Name That Number(杂题)

Among the large Wisconsin cattle ranchers, it is customary to brandcows with serial numbers to pleas...

USACO Name That Number 解题日志

先贴上题目吧。 Name That Number Among the large Wisconsin cattle ranchers, it is customary to brand...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[USACO]1.2.3 name that number
举报原因:
原因补充:

(最多只允许输入30个字)