2017年天梯赛LV2题目汇总小结
Ⅰ.L2-021 点赞狂魔---STL应用
微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明显的特性。本题就要求你写个程序,通过统计每个人点赞的不同标签的数量,找出前3名点赞狂魔。
输入格式:
输入在第一行给出一个正整数N(≤100),是待统计的用户数。随后N行,每行列出一位用户的点赞标签。
输出格式:
统计每个人点赞的不同标签的数量,找出数量最大的前3名,在一行中顺序输出他们的用户名,其间以1个空格分隔,且行末不得有多余空格。如果有并列,则输出标签出现次数平均值最小的那个,题目保证这样的用户没有并列。若不足3人,则用-补齐缺失,例如mike jenny -就表示只有2人。
输入样例:
5
bob 11 101 102 103 104 105 106 107 108 108 107 107
peter 8 1 2 3 4 3 2 5 1
chris 12 1 2 3 4 5 6 7 8 9 1 2 3
john 10 8 7 6 5 4 3 2 1 7 5
jack 9 6 7 8 9 10 11 12 13 14
输出样例:
jack chris john
思路:使用集合(自动去重) 统计每个人点赞不同博文的数量。输出前3个人,不足3人输出“-”
#include<bits/stdc++.h>
using namespace std;
int n;
set<int> se;
struct node{
int size;
string name;
int aclSize;
};
node ans[105];
bool cmp(node a,node b){
if(a.size == b.size){
return a.aclSize < b.aclSize;
}
return a.size > b.size;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
string name;
cin>>name;
ans[i].name = name;
int k;
cin>>k;
ans[i].aclSize = k;
for(int j=1;j<=k;j++){
int d;
cin>>d;
se.insert(d);
}
ans[i].size = se.size();
se.clear();
}
sort(ans+1,ans+n+1,cmp);
int first = 1;
for(int i=1;i<=3 && i<=n;i++){
if(first){
cout<<ans[i].name;
first = 0;
}else{
cout<<" "<<ans[i].name;
}
}
if(n<3){
for(int i=n+1;i<=3;i++){
cout<<" "<<"-";
}
}
return 0;
}
Ⅱ.L2-022 重排链表--数组模拟链表
给定一个单链表 L
请编写程序将链表重新排列为 L
例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。
输入格式:
每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N (≤105)。结点的地址是5位非负整数,NULL地址用−1表示。
接下来有N行,每行格式为:
Address Data Next
其中Address是结点地址;Data是该结点保存的数据,为不超过10
5
的正整数;Next是下一结点的地址。题目保证给出的链表上至少有两个结点。
输出格式:
对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。
输入样例:
00100 6
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
输出样例:
68237 6 00100
00100 1 99999
99999 5 12309
12309 2 00000
00000 4 33218
33218 3 -1
思路:结构体模拟链表,具体做法不太好表达,看代码吧
转载至:https://blog.csdn.net/hy971216/article/details/80496917
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e5;
struct Node{
int address;
int key;
int next;
int num; //结点的最终位置下标
}node[maxn]; //存放数据 后面改变下标后也存放输出结果
bool vis[maxn];
bool cmp(Node a,Node b){
return a.num<b.num;
}
int main()
{
int head,n,a;
scanf("%d%d",&head,&n);
int k1=0,k2=0;
for(int i=0;i<maxn;i++){
node[i].num=2*maxn;//初始化 num为最大
}
for(int i=0;i<n;i++){
scanf("%d",&a);//地址作为下标
scanf("%d%d",&node[a].key,&node[a].next);
node[a].address=a;
}
for(int i=head;i!=-1;i=node[i].next){
if(!vis[abs(node[i].key)]){ //没有出现过当前结点的绝对值 就从下标0开始按顺序存放到结构体数组中
vis[abs(node[i].key)]=true;
node[i].num=k1;
k1++;
}else{
node[i].num=maxn+k2;//出现过 就从下标maxn开始存放 因为最大也就maxn个结点
k2++;
}
}
sort(node,node+maxn,cmp);//按num排序
int k=k1+k2;
for(int i=0;i<k;i++){
if(i!=k1-1&&i!=k-1){
printf("%05d %d %05d\n",node[i].address,node[i].key,node[i+1].address); // %05d输出5个长度整数 不足5为补0
}else{
printf("%05d %d -1\n",node[i].address,node[i].key);
}
}
return 0;
}
Ⅲ.L2-023 图着色问题---图着色dfs、bfs、邻接点问题
图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?
但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。
输入格式:
输入在第一行给出3个整数V(0<V≤500)、E(≥0)和K(0<K≤V),分别是无向图的顶点数、边数、以及颜色数。顶点和颜色都从1到V编号。随后E行,每行给出一条边的两个端点的编号。在图的信息给出之后,给出了一个正整数N(≤20),是待检查的颜色分配方案的个数。随后N行,每行顺次给出V个顶点的颜色(第i个数字表示第i个顶点的颜色),数字间以空格分隔。题目保证给定的无向图是合法的(即不存在自回路和重边)。
输出格式:
对每种颜色分配方案,如果是图着色问题的一个解则输出Yes,否则输出No,每句占一行。
输入样例:
6 8 3
2 1
1 3
4 6
2 5
2 4
5 4
5 6
3 6
4
1 2 3 3 1 2
4 5 6 6 4 5
1 2 3 4 5 6
2 3 4 2 3 4
输出样例:
Yes
Yes
No
No
思路:只需判断邻接点是否颜色相等,bfs和dfs或者直接暴力看邻接边的两端点。
还有一些特殊样例:当k(颜色数) != 输入的颜色数 也不符合条件
dfs做法 23分 wa了一个点:
#include<bits/stdc++.h>
using namespace std;
vector<int> g[510];
int v,e,k,n;
set<int> se;
int vis[510];
int col[510];
bool flag = true;
void dfs(int x){
if(!flag)return;
vis[x] = 1;
int color = col[x];
for(int i=0;i<g[x].size();i++){
if(!vis[g[x][i]]){
if(color == col[g[x][i]])
flag = false;
else
dfs(g[x][i]);
}
}
}
int main(){
cin>>v>>e>>k;
for(int i=1;i<=e;i++){
int d1,d2;
cin>>d1>>d2;
g[d1].push_back(d2);
g[d2].push_back(d1);
}
cin>>n;
while(n--){
memset(vis,0,sizeof(vis));
for(int i=1;i<=v;i++){
int d;
cin>>d;
se.insert(d);
col[i] = d;
}
if(se.size()!=k){
cout<<"No"<<endl;
}else{
flag = true;
for(int i=1;i<=v;i++){
if(!vis[i] && flag){
dfs(i);
}
}
if(flag){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
se.clear();
}
return 0;
}
其他暴力做法—查看邻接矩阵中的 邻接边两端点颜色是否相等
参考代码:https://blog.csdn.net/guoqingshuang/article/details/80575008
Ⅳ.L2-024 部落--并查集
在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。
输入格式:
输入在第一行给出一个正整数N(≤104),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:
K P[1] P[2] ⋯ P[K]
其中K是小圈子里的人数,P[i](i=1,⋯,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过104。
之后一行给出一个非负整数Q(≤104),是查询次数。随后Q行,每行给出一对被查询的人的编号。
输出格式:
首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出N。
输入样例:
4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7
输出样例:
10 2
Y
N
思路:并查集,同一部落的属于同一集合
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int n;
int father[maxn];
set<int> se;
set<int> se2;
//查找
int find(int x){
if(father[x]==x) return x;
return father[x]=find(father[x]);
}
//合并
void unite(int x,int y){
x=find(x);
y=find(y);
if(x!=y)
father[x]=y;
}
//初始化
void init(){
for(int i=1;i<=maxn-5;i++){ //注意这里初始化 不能初始化成maxn ----段错误有时也不提醒..大坑
father[i] = i;
}
}
int main(){
cin>>n;
init();
for(int i=1;i<=n;i++){
int k;
cin>>k;
int d;
cin>>d;
se.insert(d);//se集合中加入第一个元素
int fa = d;
for(int j=2;j<=k;j++){
cin>>d;
se.insert(d);
unite(fa,d);//合并
}
}
for(set<int>::iterator it = se.begin();it!=se.end();it++){
se2.insert(find(*it));//se2集合查询 有多少个根(部落数量)
}
printf("%d %d\n",se.size(),se2.size());
int q;
cin>>q;
while(q--){
int pa,pb;
cin>>pa>>pb;
pa = find(pa);
pb = find(pb);
if(pa==pb){
puts("Y");
}else{
puts("N");
}
}
return 0;
}