L1-046 整除光棍 (20 分)
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s,表示x乘以s是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。
提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。
输入格式:
输入在一行中给出一个不以5结尾的正奇数x(<1000)。
输出格式:
在一行中输出相应的最小的s和n,其间以1个空格分隔。
输入样例:
31
输出样例:
3584229390681 15
主要思路:
模拟除法的过程,因为每一位都是1,所以可以先列举出最高位后边有余数时就在后边补一,恰好整除的时候结束循环
#include<iostream>
using namespace std;
int n,r=1,w=1;
int main()
{
cin>>n;
while(r<n)
{
r*=10;
r++;
w++;//记录位数
}
while(1)
{
cout<<r/n;//模拟除法的过程
r%=n;
if(r==0)
break;
r=r*10+1;
w++;
}
cout<<' '<<w<<endl;
return 0;
}
L1-064 估值一亿的AI核心代码 (20 分)
本题要求你实现一个稍微更值钱一点的 AI 英文问答程序,规则是:
无论用户说什么,首先把对方说的话在一行中原样打印出来;
消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
把原文中所有大写英文字母变成小写,除了 I;
把原文中所有独立的 can you、could you 对应地换成 I can、I could—— 这里“独立”是指被空格或标点符号分隔开的单词;
把原文中所有独立的 I 和 me 换成 you;
把原文中所有的问号 ? 换成惊叹号 !;
在一行中输出替换后的句子作为 AI 的回答。
输入格式:
输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。
输出格式:
按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。
输入样例:
6
Hello ?
Good to chat with you
can you speak Chinese?
Really?
Could you show me 5
What Is this prime? I,don 't know
输出样例:
Hello ?
AI: hello!
Good to chat with you
AI: good to chat with you
can you speak Chinese?
AI: I can speak chinese!
Really?
AI: really!
Could you show me 5
AI: I could show you 5
What Is this prime? I,don 't know
AI: what Is this prime! you,don’t know
主要思路:
正则表达式
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
cin.get();
while (n--) {
string s;
getline(cin, s);
cout << s << endl;
for (char & c : s) {
if (c >='A' && c <= 'Z' && c != 'I') {
c = c - 'A' + 'a';
}
}
s = regex_replace(s, regex(" +"), " ");//消除多余空格
s = regex_replace(s, regex("^ | $"), "");//消除首尾空格
s = regex_replace(s, regex(" (\\W)"), "$1");//消除标点符号前的空格
s = regex_replace(s, regex("\\?"), "!");//将?替换成!
s = regex_replace(s, regex("\\bcan you\\b"), "A");//转换can you
s = regex_replace(s, regex("\\bcould you\\b"), "B");//转换could you
s = regex_replace(s, regex("\\b(I|me)\\b"), "C");//转换I、me
s = regex_replace(s, regex("A"), "I can");
s = regex_replace(s, regex("B"), "I could");
s = regex_replace(s, regex("C"), "you");
cout << "AI: " << s << endl;
}
return 0;
}
L2-001 紧急救援 (25 分)
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
结尾无空行
输出样例:
2 60
0 1 3
结尾无空行
主要思路:
加入细节处理的最短路问题。
细节处理以及路径的标记太妙了,具体细节看代码。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> pa;
const int N=1010;
int n,m,s,e;
vector<int> l[N],w[N];
int numsum[N],num[N],dis[N],rownum[N],pre[N];
bool book[N];
void printpath(int x)
{
if(x==s)
{
cout<<x;
return;
}
printpath(pre[x]);
cout<<' '<<x;
}
void dj()
{
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
numsum[s]=num[s];
rownum[s]=1;
priority_queue<pa,vector<pa>,greater<pa> >q;
q.push({0,s});
while(q.size())
{
pa now=q.top();
q.pop();
int ll=now.second,diss=now.first;
if(book[ll]) continue;
book[ll]=1;
for(int i=0;i<l[ll].size();i++)
{
int lll=l[ll][i],disss=w[ll][i];
if(dis[lll]>diss+disss)
{
dis[lll]=diss+disss;
numsum[lll]=numsum[ll]+num[lll];
rownum[lll]=rownum[ll];
pre[lll]=ll;
q.push({dis[lll],lll});
}
else if(dis[lll]==diss+disss)
{
rownum[lll]+=rownum[ll];
if(numsum[lll]<numsum[ll]+num[lll])
{
numsum[lll]=numsum[ll]+num[lll];
pre[lll]=ll;
q.push({dis[lll],lll});
}
}
}
}
}
int main()
{
cin>>n>>m>>s>>e;
for(int i=0;i<n;i++)
cin>>num[i];
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
l[x].push_back(y);
w[x].push_back(z);
l[y].push_back(x);
w[y].push_back(z);
}
dj();
cout<<rownum[e]<<' '<<numsum[e]<<endl;
printpath(e);
return 0;
}
L2-004 这是二叉搜索树吗? (25 分)
一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
其左子树中所有结点的键值小于该结点的键值;
其右子树中所有结点的键值大于等于该结点的键值;
其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数 N(≤1000)。随后一行给出 N 个整数键值,其间以空格分隔。
输出格式:
如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出 YES ,然后在下一行输出该树后序遍历的结果。数字间有 1 个空格,一行的首尾不得有多余空格。若答案是否,则输出 NO。
输入样例 1:
7
8 6 5 7 10 8 11
结尾无空行
输出样例 1:
YES
5 7 6 8 11 10 8
主要思路:
前序遍历结果的特点是第一个为根节点,然后是左子树和右子树。二叉搜索树前序遍历的特点是第一个节点(根节点)比左子树大,比右子树小,那么定义两个变量i、j分别在待确定的前序数组从左到右遍历和从右向左,确定左儿子和右儿子区间,如果i-j=1,那么肯定对的,否则,就不是前序遍历。镜像的话大于左子树小于右子树换一下就行。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1010;
vector<int> ans;
bool flag;
int a[N];
void find(int l,int r)
{
if(l>r) return;
int i=l+1,j=r;
if(!flag)
{
while(j>l&&a[j]>=a[l]) j--;//注意不能判断能走再走,1,2个元素时会出错
while(i<=r&&a[i]<a[l]) i++;
}
else
{
while(j>l&&a[j]<a[l]) j--;
while(i<=r&&a[i]>=a[l]) i++;
}
if(i-j!=1) return;
find(l+1,j);
find(i,r);
ans.push_back(a[l]);
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
find(1,n);
if(ans.size()!=n)
{
flag=1;
ans.clear();
find(1,n);
}
if(ans.size()!=n)
cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
for(int i=0;i<ans.size();i++)
{
cout<<ans[i];
if(i!=ans.size()-1)
cout<<' ';
}
}
return 0;
}
L2-005 集合相似度 (25 分)
给定两个整数集合,它们的相似度定义为:Nc /Nt×100%。其中Nc是两个集合都有的不相等整数的个数,Nt是两个集合一共有的不相等整数的个数。你的任务就是计算任意一对给定集合的相似度。
输入格式:
输入第一行给出一个正整数N(≤50),是集合的个数。随后N行,每行对应一个集合。每个集合首先给出一个正整数M(≤10^4), 是集合中元素的个数后跟M个[0,10^9]区间内的整数。
之后一行给出一个正整数K(≤2000),随后K行,每行对应一对需要计算相似度的集合的编号(集合从1到N编号)。数字间以空格分隔。
输出格式:
对每一对需要计算的集合,在一行中输出它们的相似度,为保留小数点后2位的百分比数字。
输入样例:
3
3 99 87 101
4 87 101 5 87
7 99 101 18 5 135 18 99
2
1 2
1 3
输出样例:
50.00%
33.33%
主要思路:
寻找并集和交集
主要问题是如何除重,利用set容器
#include<bits/stdc++.h>
using namespace std;
int main()
{
map<int,set<int> > op;
int n;
cin>>n;
int m,k;
for(int i=1;i<=n;i++)
{
cin>>m;
for(int j=0;j<m;j++)
{
cin>>k;
op[i].insert(k);
}
}
int oo;
cin>>oo;
double kk;
int l,u;
for(int i=0;i<oo;i++)
{
kk=0;
cin>>l>>u;
for(set<int>::iterator j=op[l].begin();j!=op[l].end();j++)
{
if(op[u].count(*j)) kk++;
}
printf("%.2f%\n",kk/(op[l].size()+op[u].size()-kk)*100);
}
return 0;
}
L2-012 关于堆的判断 (25 分)
将一系列给定数字顺序插入一个初始为空的小顶堆H[]。随后判断一系列相关命题是否为真。命题分下列几种:
x is the root:x是根结点;
x and y are siblings:x和y是兄弟结点;
x is the parent of y:x是y的父结点;
x is a child of y:x是y的一个子结点。
输入格式:
每组测试第1行包含2个正整数N(≤ 1000)和M(≤ 20),分别是插入元素的个数、以及需要判断的命题数。下一行给出区间[−10000,10000]内的N个要被插入一个初始为空的小顶堆的整数。之后M行,每行给出一个命题。题目保证命题中的结点键值都是存在的。
输出格式:
对输入的每个命题,如果其为真,则在一行中输出T,否则输出F。
输入样例:
5 4
46 23 26 24 10
24 is the root
26 and 23 are siblings
46 is the parent of 23
23 is a child of 10
结尾无空行
输出样例:
F
T
F
T
主要思路:
开始不了解堆的结构,做完这个题才知道,堆的内部结构只有一个要求就是根节点都比孩子大(或者是小),那么堆构造起来就非常容易了:
不断地往(开始为空的)堆里边插入数值,若是它比它直接插入所在的位置的父节点大(或者是小),那么直接将它与父节点调换就好了。
还是这个题还涉及到了字符串读入与识别问题,具体细节看代码。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
int a[1010],cnt;
map<int,int> p;
void creat(int x)
{
a[++cnt]=x;
int t=cnt;
while(a[t]<a[t/2]&&t>1)
{
swap(a[t],a[t/2]);
t/=2;
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
creat(x);
}
for(int i=1;i<=n;i++)
p[a[i]]=i;
while(m--)
{
int x,y;
cin>>x;
string s;
cin>>s;
if(s[0]=='a')
{
cin>>y;
getline(cin,s);
if(p[x]/2==p[y]/2)
cout<<'T'<<endl;
else cout<<'F'<<endl;
continue;
}
cin>>s;
if(s[0]=='a')
{
cin>>s;
cin>>s;
cin>>y;
if(p[x]/2==p[y])
cout<<'T'<<endl;
else cout<<'F'<<endl;
continue;
}
cin>>s;
if(s[0]=='r')
{
char aa=p[x]==1?'T':'F';
cout<<aa<<endl;
continue;
}
cin>>s;
cin>>y;
if(p[y]/2==p[x])
cout<<'T'<<endl;
else cout<<'F'<<endl;
}
return 0;
}
L2-013 红色警报 (25 分)
战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。
输入格式:
输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。
注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。
输出格式:
对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.。
输入样例:
5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3
输出样例:
City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.
主要思路:
这是一道并查集的问题,先初始化,再依次求被攻占0~k个城市时的不联通量,如果不联通量不变(该城市完全独立),或不联通量加一(仅该被攻占的城市脱离)则不发出红色预警
#include<iostream>
using namespace std;
int f[100001],n,m,p[100001],map[1001][2];
int find(int k)
{
if(k==f[k]) return k;
return f[k]=find(f[k]);
}
void fir()
{
for(int i=0;i<n;i++)
f[i]=i;
}
void uni(int a,int b)
{
if(find(f[a])!=find(f[b]))
f[find(f[a])]=find(f[b]);
}
int ff()
{
int ans=0;
for(int i=0;i<n;i++)
if(f[i]==i)
ans++;
return ans;
}
int main()
{
cin>>n>>m;
fir();
for(int i=0;i<m;i++)
{
cin>>map[i][0]>>map[i][1];
uni(map[i][0],map[i][1]);
}
int c1=ff();
int k;
cin>>k;
for(int i=0;i<k;i++)
{
fir();
int x;
cin>>x;
p[x]=1;
for(int j=0;j<m;j++)//这里记得存储,因为要进行k次重复运算
{
if(p[map[j][0]]||p[map[j][1]]);
else
uni(map[j][0],map[j][1]);
}
int c2=ff();
if(c1==c2||c1==c2-1)
cout<<"City "<<x<<" is lost."<<endl;
else
cout<<"Red Alert: City "<<x<<" is lost!"<<endl;
c1=c2;
}
if(c1==n)
cout<<"Game Over."<<endl;
return 0;
}
L2-014 列车调度 (25 分)
两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有9趟列车,在入口处按照{8,4,2,5,3,9,1,6,7}的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?
输入格式:
输入第一行给出一个整数N (2 ≤ N ≤10^5 ),下一行给出从1到N的整数序号的一个重排列。数字间以空格分隔。
输出格式:
在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。
输入样例:
9
8 4 2 5 3 9 1 6 7
输出样例:
4
主要思路:
保存每个隧道尾部的最小值
#include<iostream>
#include<set>
using namespace std;
int main()
{
int n;
set<int> st;
cin>>n;
for(int i=0;i<n;i++)
{
int k;
cin>>k;
set<int>::iterator x=st.lower_bound(k);
st.insert(k);
if(x!=st.end())//如果不是比所有的尾部最小值都大
st.erase(x);//不用新开隧道
}
cout<<st.size();//每个元素都是不同隧道的尾部最小值
return 0;
}
L2-016 愿天下有情人都是失散多年的兄妹 (25 分)
主要思路:
利用递归的方法依次查询五代的关系
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
struct pop
{
string fa="-1",mo="-1";
char sex;
};
map<string,pop> a;
bool can(string A,string B,int time)
{
if(A=="-1"||B=="-1") return 1; //如果该人不存在
else if(time>=5)//注意,time=1时查询的是第二代
return 1;
else if((a[A].fa!="-1"&&a[B].fa==a[A].fa)||(a[A].mo!="-1"&&a[A].mo==a[B].mo))//同父或同母
return 0;
time++;
return can(a[A].fa,a[B].fa,time)&&can(a[A].fa,a[B].mo,time)&&can(a[A].mo,a[B].fa,time)&&can(a[A].mo,a[B].mo,time);//继续下一代
}
int main()
{
int n;
cin>>n;
string me,ma,ba;
char s;
for(int i=0;i<n;i++)
{
cin>>me>>s>>ba>>ma;
a[me].sex=s;
a[me].fa=ba;
a[me].mo=ma;
a[ba].sex='M';//性别初始化,防止错误的判断是否同性
a[ma].sex='F';
}
int k;
cin>>k;
string aa,bb;
for(int i=0;i<k;i++)
{
cin>>aa>>bb;
if(a[aa].sex==a[bb].sex)
cout<<"Never Mind"<<endl;
else
{
if(can(aa,bb,1))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
}
return 0;
}
L2-023 图着色问题 (25 分)
图着色问题是一个著名的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
主要思路:
有两种no的情况:颜色数不同或相邻节点颜色相同
利用set除重判断颜色数
利用vector循环判断联通的节点颜色是否相同
#include<iostream>
#include<vector>
#include<set>
using namespace std;
vector<int> a[100001];
int color[100001],flag,n;
set<int>aa;
void pd()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<a[i].size();j++)
{
int v=a[i][j];
if(color[i]==color[v])
{
flag=0;
}
}
}
}
int main()
{
int m,p;
cin>>n>>m>>p;
while(m--)
{
int x,y;
cin>>x>>y;
a[x].push_back(y);
a[y].push_back(x);
}
int nn;
cin>>nn;
while(nn--)
{
aa.clear();
for(int i=1;i<=n;i++)
{
cin>>color[i];
aa.insert(color[i]);
}
if(aa.size()!=p)
cout<<"No"<<endl;
else
{
flag=1;
pd();
if(!flag)
cout<<"No"<<endl;
else
cout<<"Yes"<<endl;
}
}
return 0;
}
L2-025 分而治之 (25 分)
分而治之,各个击破是兵家常用的策略之一。在战争中,我们希望首先攻下敌方的部分城市,使其剩余的城市变成孤立无援,然后再分头各个击破。为此参谋部提供了若干打击方案。本题就请你编写程序,判断每个方案的可行性。
输入格式:
输入在第一行给出两个正整数 N 和 M(均不超过10 000),分别为敌方城市个数(于是默认城市从 1 到 N 编号)和连接两城市的通路条数。随后 M 行,每行给出一条通路所连接的两个城市的编号,其间以一个空格分隔。在城市信息之后给出参谋部的系列方案,即一个正整数 K (≤ 100)和随后的 K 行方案,每行按以下格式给出:
Np v[1] v[2] … v[Np]
其中 Np 是该方案中计划攻下的城市数量,后面的系列 v[i] 是计划攻下的城市编号。
输出格式:
对每一套方案,如果可行就输出YES,否则输出NO。
输入样例:
10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 10
2 4
5
4 10 3 8 4
6 6 1 7 5 4 9
3 1 8 4
2 2 8
7 9 8 7 6 5 4 2
输出样例:
NO
YES
YES
NO
NO
主要思路:
看到联通关系想到并查集
以为类似于”红色警报“,可是这个每个情况都是独立的,需要恢复却无法恢复,所以直接递归判断是否该条件下有联通的城市
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int nnn;
vector<int>a[1000001];
bool book[1000001];
bool judge()
{
for(int i=1;i<=nnn;i++)
for(int j=0;j<a[i].size();j++)
if(book[i]==0&&book[a[i][j]]==0)
return 0;
return 1;
}
int main()
{
int m;
cin>>nnn>>m;
while(m--)
{
int x,y;
cin>>x>>y;
a[x].push_back(y);
a[y].push_back(x);
}
int n;
cin>>n;
while(n--)
{
memset(book,0,sizeof(book));
int nn;
cin>>nn;
while(nn--)
{
int xx;
cin>>xx;
book[xx]=1;
}
if(judge())
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
L2-026 小字辈 (25 分)
本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。
输入格式:
输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号,其中第 i 个编号对应第 i 位成员的父/母。家谱中辈分最高的老祖宗对应的父/母编号为 -1。一行中的数字间以空格分隔。
输出格式:
首先输出最小的辈分(老祖宗的辈分为 1,以下逐级递增)。然后在第二行按递增顺序输出辈分最小的成员的编号。编号间以一个空格分隔,行首尾不得有多余空格。
输入样例:
9
2 6 5 5 -1 5 6 4 7
输出样例:
4
1 9
主要思路:
利用动态数组存储某一编号的子代,再进行dfs从根(祖先的下一辈,即第二辈)开始寻找,直到找到没有下一辈的那一辈,过程中将,每一个编号都存储辈分,最后遍历所有编号输出最小辈分的编号。
#include<iostream>
#include<vector>
using namespace std;
vector<int> a[100001];
int s[1000001],mina=1;
void dfs(int x,int step)
{
s[x]=step;
if(a[x].size()==0)
{
mina=max(mina,step);
return;
}
for(int i=0;i<a[x].size();i++)
{
if(!s[a[x][i]])
dfs(a[x][i],step+1);
}
}
int main()
{
int n,m,root;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>m;
if(m==-1)
root=i;
else
a[m].push_back(i);
}
dfs(root,2);
cout<<mina-1<<endl;
int flag=0;
for(int i=1;i<=n;i++)
if(s[i]==mina)
{
if(!flag)
{
cout<<i;
flag=1;
}
else
cout<<' '<<i;
}
return 0;
}
L2-029 特立独行的幸福 (25 分)
对一个十进制数的各位数字做一次平方和,称作一次迭代。如果一个十进制数能通过若干次迭代得到 1,就称该数为幸福数。1 是一个幸福数。此外,例如 19 经过 1 次迭代得到 82,2 次迭代后得到 68,3 次迭代后得到 100,最后得到 1。则 19 就是幸福数。显然,在一个幸福数迭代到 1 的过程中经过的数字都是幸福数,它们的幸福是依附于初始数字的。例如 82、68、100 的幸福是依附于 19 的。而一个特立独行的幸福数,是在一个有限的区间内不依附于任何其它数字的;其独立性就是依附于它的的幸福数的个数。如果这个数还是个素数,则其独立性加倍。例如 19 在区间[1, 100] 内就是一个特立独行的幸福数,其独立性为 2×4=8。
另一方面,如果一个大于1的数字经过数次迭代后进入了死循环,那这个数就不幸福。例如 29 迭代得到 85、89、145、42、20、4、16、37、58、89、…… 可见 89 到 58 形成了死循环,所以 29 就不幸福。
本题就要求你编写程序,列出给定区间内的所有特立独行的幸福数和它的独立性。
输入格式:
输入在第一行给出闭区间的两个端点:1<A<B≤10
4
。
输出格式:
按递增顺序列出给定闭区间 [A,B] 内的所有特立独行的幸福数和它的独立性。每对数字占一行,数字间以 1 个空格分隔。
如果区间内没有幸福数,则在一行中输出 SAD。
输入样例 1:
10 40
输出样例 1:
19 8
23 6
28 3
31 4
32 3
注意:样例中,10、13 也都是幸福数,但它们分别依附于其他数字(如 23、31 等等),所以不输出。其它数字虽然其实也依附于其它幸福数,但因为那些数字不在给定区间 [10, 40] 内,所以它们在给定区间内是特立独行的幸福数。
输入样例 2:
110 120
输出样例 2:
SAD
主要思路:
利用树的结构
#include<iostream>
using namespace std;
int fa[10010],n,m;
int getfa(int x)
{
int sum=0;
while(x)
{
sum+=(x%10)*(x%10);
x/=10;
}
return sum;
}
int find_root(int x)
{
int ans=x;
int i=0;
while(fa[ans]!=ans)//找到根节点
{
ans=fa[ans];
i++;
if(i>10000)
return -1;
}
return ans;
}
bool fun(int x)//找到是否在nm范围内有与x有关的数
{
for(int i=n;i<=m;i++)
if(fa[i]==x)该数不是由任何一个数各位平方得来的
return false;
return true;
}
bool sushu(int x)
{
for(int i=2;i*i<x;i++)
if(x%i==0)
return false;
return true;
}
int main()
{
cin>>n>>m;
int c=0;
for(int i=1;i<=10000;i++)//求出范围内所有数的各位平方和
fa[i]=getfa(i);
for(int i=n;i<=m;i++)
{
if(fun(i)&&find_root(i)==1)
{
cout<<i<<' ';
int flag=1,x=i;
while(getfa(x)!=1)//判断几步到达根节点
{
x=getfa(x);
flag++;
}
if(sushu(i)) flag*=2;
cout<<flag<<endl;
c++;
}
}
if(!c)
cout<<"SAD"<<endl;
return 0;
}