编程之美大赛资格赛的一点整理

来源:互联网 发布:斯维尔软件 编辑:程序博客网 时间:2024/05/18 01:37

资格赛只要做出一道题便可以,因为LOL比赛的关系也只是草草的做了一下,发现微软出题还是很有趣的,所以来整理一下~~~~(>_<)~~~~ 

发现微软的面试题目,很侧重于先推导中间结论,简化算法复杂度的思路,这也是我很喜欢的,虽然我的数学很弱。⊙﹏⊙b

//--------------------------------------------------------------------------------------格叽格叽------------------------------------------------------------------------------------------------------------------

传话游戏

描述

Alice和Bob还有其他几位好朋友在一起玩传话游戏。这个游戏是这样进行的:首先,所有游戏者按顺序站成一排,Alice站第一位,Bob站最后一位。然后,Alice想一句话悄悄告诉第二位游戏者,第二位游戏者又悄悄地告诉第三位,第三位又告诉第四位……以此类推,直到倒数第二位告诉Bob。两位游戏者在传话中,不能让其他人听到,也不能使用肢体动作来解释。最后,Bob把他所听到的话告诉大家,Alice也把她原本所想的话告诉大家。 


由于传话过程中可能出现一些偏差,游戏者越多,Bob最后听到的话就与Alice所想的越不同。Bob听到的话往往会变成一些很搞笑的东西,所以大家玩得乐此不疲。经过几轮游戏后,Alice注意到在两人传话中,有些词汇往往会错误地变成其他特定的词汇。Alice已经收集到了这样的一个词汇转化的列表,她想知道她的话传到Bob时会变成什么样子,请你写个程序来帮助她。

输入

输入包括多组数据。第一行是整数 T,表示有多少组测试数据。每组数据第一行包括两个整数 N 和 M,分别表示游戏者的数量和单词转化列表长度。随后有 M 行,每行包含两个用空格隔开的单词 a 和 b,表示单词 a 在传话中一定会变成 b。输入数据保证没有重复的 a。最后一行包含若干个用单个空格隔开的单词,表示Alice所想的句子,句子总长不超过100个字符。所有单词都只包含小写字母,并且长度不超过20,同一个单词的不同时态被认为是不同的单词。你可以假定不在列表中的单词永远不会变化。

输出

对于每组测试数据,单独输出一行“Case #c: s”。其中,c 为测试数据编号,s 为Bob所听到的句子。s 的格式与输入数据中Alice所想的句子格式相同。

数据范围

1 ≤ T ≤ 5


小数据:1 ≤ N ≤ 100, 1 ≤ M ≤ 100, 1 ≤ len ≤ 10000


大数据:1 ≤ N ≤ 100000, 1 ≤ M ≤ 100000, 1 ≤ len ≤ 1000000000



思路以及代码

这道题没有什么难度,就是一个简单的模拟,注意读懂题意就可以啦。
#include <iostream>#include <map>#include <sstream>using namespace std;int T,C;int n,m;string tarstr;stringstream ss;string t,tt;map<string,string> varstr;int main(){    cin>>T;    C=0;    while(T--){        C++;        cin>>n>>m;        varstr.clear();        for (int i=0;i<m;i++){            cin>>t>>tt;            varstr[t]=tt;        }        cin.ignore(256,'\n');        getline(cin,tarstr);        ss.clear();        ss<<tarstr;        cout<<"Case #"<<C<<":";        while(ss>>tarstr){             for (int i=0;i<n-1;i++){                if (varstr.find(tarstr)==varstr.end()) break;                tarstr=varstr[tarstr];             }            cout<<" "<<tarstr;        }        cout<<endl;    }}

长方形

描述

在 N 条水平线与 M 条竖直线构成的网格中,放 K 枚石子,每个石子都只能放在网格的交叉点上。问在最优的摆放方式下,最多能找到多少四边平行于坐标轴的长方形,它的四个角上都恰好放着一枚石子。

输入

输入文件包含多组测试数据。
第一行,给出一个整数T,为数据组数。接下来依次给出每组测试数据。
每组数据为三个用空格隔开的整数 N,M,K。

输出

对于每组测试数据,输出一行"Case #X: Y",其中X表示测试数据编号,Y表示最多能找到的符合条件的长方形数量。所有数据按读入顺序从1开始编号。

数据范围

1 ≤ T ≤ 100
0 ≤ K ≤ N * M
小数据:0 < N, M ≤ 30
大数据:0 < N, M ≤ 30000

思路及代码

这道题是一道数学题,简单的枚举再加上数学公式就可以做,麻烦的地方在于一些边界的计算,很容易出错。
#include <iostream>#include <map>#include <sstream>#include <math.h>using namespace std;int T,C;long n,m,k,ans,t,nt,mt,kt,addm,addn;long max(long a,long b){    if (a<b) return b;    else return a;}int main(){    cin>>T;    for (C=1;C<=T;C++){        cin>>n>>m>>k;        if (n<m) {            t=n;            n=m;            m=t;        }        ans=0;        for (nt=1;nt<=n;nt++){            t=0;            mt=k / nt;            if ((mt<=m) && (0<mt)){                t+=(mt-1)*(nt-1)*mt*nt>>2;                kt=k-nt*mt;                addm=0;                addn=0;                if (0<kt){                    if ((kt<=mt) && (nt+1<=n))                        addn=nt*kt*(kt-1)>>1;                    if ((kt<=nt) && (mt+1<=m))                        addm=mt*kt*(kt-1)>>1;                    t+=max(addn,addm);                }            }            ans = max(ans,t);        }        cout<<"Case #"<<C<<": "<<ans<<endl;    }}


树上的三角形

描述

有一棵树,树上有只毛毛虫。它在这棵树上生活了很久,对它的构造了如指掌。所以它在树上从来都是走最短路,不会绕路。它还还特别喜欢三角形,所以当它在树上爬来爬去的时候总会在想,如果把刚才爬过的那几根树枝/树干锯下来,能不能从中选三根出来拼成一个三角形呢?

输入

输入数据的第一行包含一个整数 T,表示数据组数。
接下来有 T 组数据,每组数据中:
第一行包含一个整数 N,表示树上节点的个数(从 1 到 N 标号)。
接下来的 N-1 行包含三个整数 a, b, len,表示有一根长度为 len 的树枝/树干在节点 a 和节点 b 之间。
接下来一行包含一个整数 M,表示询问数。
接下来M行每行两个整数 S, T,表示毛毛虫从 S 爬行到了 T,询问这段路程中的树枝/树干是否能拼成三角形。

输出

对于每组数据,先输出一行"Case #X:",其中X为数据组数编号,从 1 开始。
接下来对于每个询问输出一行,包含"Yes"或“No”,表示是否可以拼成三角形。

数据范围

1 ≤ T ≤ 5
小数据:1 ≤ N ≤ 100, 1 ≤ M ≤ 100, 1 ≤ len ≤ 10000
大数据:1 ≤ N ≤ 100000, 1 ≤ M ≤ 100000, 1 ≤ len ≤ 1000000000

思路以及代码

这道题还是比较有趣的,当时只想出了小数据的思路,索性就没有提交,之后研究了一下最近公共祖先的算法想做一下预处理,也是不得要领。
后来经大神点悟,
是这样的
是这样的
是这样的
斐波那契数列,正好是一个不能构成三角形的数列,这个数列会很快的增大,超过题目给出的数据范围,所以大概深搜到50的深度就可以判断答案为YES了。
不到50的话也可以用斐波那契数列来判断,是否可以组成三角形
也可以这样剪枝,找出路径中最小的数和最大的数,求他们在数列中的位置,如果路经长度大于位置差,就一定可以组成三角形。
int calc(int x, int y){     int a = x, b = x, res = 2, c;    while(b <= y){c = b; b = a + b; a = c; res++; }    return res;}
其他路径很短的情况,需要排序来判断。

因为自己比较懒,木有实现,所以贴出大神的代码
//第三题来自mochavic#include <stdio.h>#include <vector>#include <algorithm>using namespace std;int deep[100010], f[100010][2];vector<int> e[100010];int c[60], cn;void dfs(int fa, int x, int d){    int i, y;    deep[x] = d;    for (i = 0; i < (int)e[x].size(); i += 2){        y = e[x][i];        if (y == fa) continue;        f[y][0] = x;        f[y][1] = e[x][i + 1];        dfs(x, y, d + 1);    }}void pd(int x, int y){    while (deep[x] > deep[y]){        c[cn++] = f[x][1];        x = f[x][0];        if (cn == 50) return;    }    while (deep[y] > deep[x]){        c[cn++] = f[y][1];        y = f[y][0];        if (cn == 50) return;    }    while (x != y){        c[cn++] = f[x][1];        x = f[x][0];        c[cn++] = f[y][1];        y = f[y][0];        if (cn >= 50) return;    }}int main(){    int T, ri = 1, n, m, x, y, z, i;    scanf("%d", &T);    while (T--){        scanf("%d", &n);        for (i = 1; i <= n; i++) e[i].clear();        for (i = 1; i < n; i++){            scanf("%d%d%d", &x, &y, &z);            e[x].push_back(y);            e[x].push_back(z);            e[y].push_back(x);            e[y].push_back(z);        }        dfs(0, 1, 0);        printf("Case #%d:\n", ri++);        scanf("%d", &m);        while (m--){            scanf("%d%d", &x, &y);            cn = 0;            pd(x, y);            sort(c, c + cn);            for (i = 0; i + 2 < cn; i++){                if (c[i] + c[i + 1] > c[i + 2]) break;            }            if (i + 2 < cn) printf("Yes\n");            else printf("No\n");        }    }    return 0;}