团体程序设计天梯赛-L2组

团体程序设计天梯赛-L2组

--------------------------------------------------------------------------------

题目传送门

团体程序设计天梯赛-L2组
--------------------------------------------------------------------------------
更多详见>>

OJ题解系列 目录导航帖
--------------------------------------------------------------------------------

这里是团体程序设计天梯赛-L2组
团体程序设计天梯赛,简称CCCC竞赛,从总体上来看,该竞赛难度不大,掌握基础的数据结构和算法便能够取得不错的分数,L1/L2/L3按照难度递增的顺序排列,其中L1是入门级的难度,类似于PAT乙级考试前3题,占100分;L2级加入了数据结构的元素,难度和PAT乙级后2题/PAT甲级前3题难度相当,占75分,L3级加入了算法进阶的内容,难度和PAT甲级最后一题/PAT顶级的难度一致,占75分。因此,练习的时候也可参考PAT专题部分,具体内容详见OJ题解系列 目录导航帖

接下来就是题解部分了,每道算法题都标注有对应的算法标签,对于那些易错、较难或是测试点比较特殊的题目会着重标注,本章推荐的题目有:

L2-012 关于堆的判断 (25 分)堆 + 字符串
L2-013 红色警报 (25 分)并查集
L2-014 列车调度 (25 分)单调队列
L2-016 愿天下有情人都是失散多年的兄妹 (25 分)
L2-030 冰岛人 (25 分)
并查集 + DFS/BFS
L2-018 多项式A除以B (25 分)高精
L2-028 秀恩爱分得快 (25 分)字符串 + 枚举
L2-034 口罩发放 (25 分)大模拟 + 排序
L2-035 完全二叉树的层序遍历 (25 分)树,二叉树 + 遍历

--------------------------------------------------------------------------------

L2-001 紧急救援 (25 分)

算法标签: 图论 + dijkstra + dfs
注意: dijkstra跑最短路,同时统计路径数,并更新点权最大值,是一道标准模板题,具体详见数据结构专题系列-图论

#include<bits/stdc++.h>
using namespace std;
int N,M,S,D;
const int maxn = 505;
int dp[maxn];
int num[maxn];
int pre[maxn];
int weight[maxn];
bool vis[maxn];
int a[maxn];
struct node{
	int v;
	int w;
};
node temp;
vector<node> G[maxn];
void Dijkstra(int S){
	memset(vis,0,sizeof(vis));
	memset(dp,0x3f,sizeof(dp));
	memset(num,0,sizeof(num));
	memset(weight,0,sizeof(weight));
	dp[S] = 0;
	num[S] = 1;
	weight[S] = a[S];
	priority_queue<pair<int,int> > q;
	q.push(make_pair(-dp[S],S));
	while(!q.empty()){
		pair<int,int> f = q.top();
		q.pop();
		int u = f.second;
		if(vis[u]){
			continue;
		}
		vis[u] = true;
		for(int i=0;i<G[u].size();i++){
			int v = G[u][i].v;
			int w = G[u][i].w;
			if(!vis[v]){
				if(dp[u]+w<dp[v]){
					dp[v] = dp[u] + w;
					num[v] = num[u];
					weight[v] = weight[u] + a[v];
					pre[v] = u;
					q.push(make_pair(-dp[v],v));
				}else if(dp[u]+w==dp[v]){
					num[v] += num[u];
					if(weight[u]+a[v]>weight[v]){
						weight[v] = weight[u] + a[v];
						pre[v] = u;
					}
				}
			}
		}
	}
}
vector<int> ans;
void Dfs(int u){
	if(u == S){
		ans.push_back(u);
		return;
	}
	Dfs(pre[u]);
	ans.push_back(u);
}
int main(){
	cin >> N >> M >> S >> D;	
	for(int i=0;i<N;i++){
		cin >> a[i];
	} 
	for(int i=1;i<=M;i++){
		int u,v,w;
		cin >> u >> v >> w;
		temp.v = v;
		temp.w = w;
		G[u].push_back(temp);
		temp.v = u;
		G[v].push_back(temp);
	}
	Dijkstra(S);
	cout << num[D] << " " << weight[D] << endl;
	Dfs(D);
	for(int i=0;i<ans.size();i++){
		if(i==0){
			printf("%d",ans[i]);
		}else{
			printf(" %d",ans[i]);
		} 
	}
	return 0;
} 

L2-002 链表去重 (25 分)

算法标签: 模拟 + 链表

#include<bits/stdc++.h>
using namespace std;
struct node{
	int value;
	int next;
};
const int maxn = 1e5+5;
node n[maxn];

int ori_add[maxn];
int ori_value[maxn];
int post_add[maxn];
int post_value[maxn];
int cnt1 = 0;
int cnt2 = 0;
set<int> s;

int main(){
	int start;
	int N;
	cin >> start >> N;
	for(int i=0;i<N;i++){
		int y;
		scanf("%d",&y);
		scanf("%d%d",&n[y].value,&n[y].next);
	}
	while(start!=-1){
		if(s.find(abs(n[start].value))!=s.end()){
			post_add[cnt2] = start;
			post_value[cnt2] = n[start].value;
			cnt2++; 
		}else{
			ori_add[cnt1] = start;
			ori_value[cnt1] = n[start].value;
			s.insert(abs(n[start].value));
			cnt1++;
		}
		start = n[start].next;
	} 
	for(int i=0;i<cnt1;i++){
		if(i!=cnt1-1){
			printf("%05d %d %05d\n",ori_add[i],ori_value[i],ori_add[i+1]);
		}else{
			printf("%05d %d -1\n",ori_add[i],ori_value[i]);
		}
	}
	for(int i=0;i<cnt2;i++){
		if(i!=cnt2-1){
			printf("%05d %d %05d\n",post_add[i],post_value[i],post_add[i+1]);
		}else{
			printf("%05d %d -1\n",post_add[i],post_value[i],post_add[i+1]);
		}
	}
	return 0;
} 

L2-003 月饼 (25 分)

算法标签: 完全背包的简单版

#include<bits/stdc++.h>
using namespace std;
int N,D;
int maxn = 1005;
typedef struct mooncake{
	double num;
	double price;
	double singleprice;
}mooncake;
bool cmp(mooncake m1,mooncake m2){
	return m1.singleprice > m2.singleprice;
}
int main(){
	cin >> N >> D;
	mooncake m[N];
	for(int i=0;i<N;i++){
		cin >> m[i].num;
	}
	for(int i=0;i<N;i++){
		cin >> m[i].price;
	}
	for(int i=0;i<N;i++){
		m[i].singleprice = (m[i].price * 1.0) /(m[i].num * 1.0);
	}
	
	sort(m,m+N,cmp);
	
	int site = 0;
	double totalprice = 0.0;
	
	while(D!=0){
		if(m[site].num <= D){
			D -= m[site].num;
			totalprice = totalprice + m[site].price;
		}else{
			totalprice = totalprice + m[site].singleprice * D;
			D = 0;
		}
		site++;
	}
	printf("%.2lf\n",totalprice);
	
	return 0;
}

L2-004 这是二叉搜索树吗? (25 分)

算法标签: 树,二叉树 + 遍历
注意: 所谓的镜像树,其实就是指颠倒了左右子树的访问顺序,其它条件都不变,因此,本题实质上依然是考察建树+遍历

#include<bits/stdc++.h>
using namespace std;

vector<int> pre,preM,post,postM,origin;

int N; 
struct node{
	int data;
	node* lchild;
	node* rchild;
};

node* root = NULL;

void insert(node* &root,int x){
	if(root==NULL){
		node *Node = new node;
		Node->data = x;
		Node->lchild = NULL;
		Node->rchild = NULL;
		root = Node;
		return;
	}
	if(x<root->data){
		insert(root->lchild,x);
	}else{
		insert(root->rchild,x);
	}
}

int cnt = 0;

void PreOrder(node* root,vector<int> &pre){
	if(root==NULL){
		return;
	}
	pre.push_back(root->data);
	PreOrder(root->lchild,pre);
	PreOrder(root->rchild,pre);
}

void MirrorOrder(node *root,vector<int> &preM){
	if(root==NULL){
		return;
	}
	preM.push_back(root->data);
	MirrorOrder(root->rchild,preM);
	MirrorOrder(root->lchild,preM);
}

void PostOrder(node* root,vector<int> &post){
	if(root==NULL){
		return;
	}
	PostOrder(root->lchild,post);
	PostOrder(root->rchild,post);
	post.push_back(root->data);
}

void PostOrder1(node* root,vector<int> &postM){
	if(root==NULL){
		return;
	}
	PostOrder1(root->rchild,postM);
	PostOrder1(root->lchild,postM);
	postM.push_back(root->data);
}

int main(){
	cin >> N;
	for(int i=0;i<N;i++){
		int t;
		cin >> t;
		origin.push_back(t);
		insert(root,t);
	}
	
	PreOrder(root,pre);
	MirrorOrder(root,preM);
	
	if(pre==origin){
		printf("YES\n");
		PostOrder(root,post);
		for(int i=0;i<post.size();i++){
			if(i==0){
				printf("%d",post[i]);
			}else{
				printf(" %d",post[i]);
			}
		}
	}else if(preM==origin){
		printf("YES\n");
		PostOrder1(root,postM);
		for(int i=0;i<postM.size();i++){
			if(i==0){
				printf("%d",postM[i]);
			}else{
				printf(" %d",postM[i]);
			}
		}
	}else{
		printf("NO\n");
	}
	
	return 0;
}

L2-005 集合相似度 (25 分)

算法标签: 集合
注意: 本题考察的重点是集合,由于集合中的元素不能重复,因此,可用STL的set进行操作,每次查询输入两个集合的编号,我们采用枚举法判断集合的相似度,最后保留2位小数后输出

#include<bits/stdc++.h>
using namespace std;
set<int> a[55];
double query[60][60];
int main(){
    int N;
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        int M;
        scanf("%d",&M);
        for(int j=0;j<M;j++){
            int t;
            scanf("%d",&t);
            a[i].insert(t);
        }
    }
     
    int K;
    scanf("%d",&K);
    for(int i=0;i<K;i++){
        int g,h;
        scanf("%d%d",&g,&h);
        set<int>::iterator it1;
        set<int>::iterator it2;
        int Nc = 0;
        for(it1 = a[g].begin(),it2 = a[h].begin();it1!=a[g].end() && it2!=a[h].end();){
            if(*it1 == *it2){
                Nc++;
                it1++;
                it2++;
            }else if(*it1<*it2){
                it1++;
            }else{
                it2++;
            }
        }
        int Nn = a[g].size() + a[h].size() - Nc;
        double r = (Nc * 1.0) /(Nn * 1.0);
        printf("%.2lf%%\n",r*100.0);
    }
    return 0;
} 

L2-006 树的遍历 (25 分)

算法标签: 树,二叉树 + 遍历
注意: 首先输入中序遍历和后序遍历,建立一棵树,然后从根结点出发,进行层次遍历

#include<bits/stdc++.h>
using namespace std;

int post[35];
int in[35];
struct node{
	int data;
	node* lchild;
	node* rchild;
};
node* root = NULL;
node* newnode(int v){
	node* Node = new node;
	Node->data = v;
	Node->lchild = NULL;
	Node->rchild = NULL; 
	return Node;	
} 
node* Create(int inL,int inR,int postL,int postR){
	if(postL>postR){
		return NULL;
	}
	int d = post[postR];
	int k;
	for(int i=inL;i<=inR;i++){
		if(in[i]==d){
			k = i;
			break;
		}
	}
	int numleft = k-inL;
	node* Node = new node;
	Node->data = d; 
	Node->lchild = Create(inL,k-1,postL,postL+numleft-1);
	Node->rchild = Create(k+1,inR,postL+numleft,postR-1); 
	return Node;
}
queue<node*> q;
int n;
void Level(node* root){
	int T = n;
	q.push(root);
	while(!q.empty()){
		node* f = q.front();
		q.pop();
		if(T==1){
			printf("%d\n",f->data);
		}else{
			printf("%d ",f->data);
		}
		T--;
		if(f->lchild){
			q.push(f->lchild);
		}
		if(f->rchild){
			q.push(f->rchild);
		}
	}
}
int main(){
	cin >> n;
	for(int i=0;i<n;i++){
		cin >> post[i];
	}	
	for(int i=0;i<n;i++){
		cin >> in[i];
	}
	root = Create(0,n-1,0,n-1);
	Level(root);
	return 0;
}

L2-007 家庭房产 (25 分)

算法标签: 并查集 + 排序
注意: 本题极易与树、二叉树知识点混淆,该题的正确解法是并查集操作,合并parent与child,然后划定area

#include<bits/stdc++.h>
using namespace std;
struct person{
	int father;
	int mother;
	vector<int> child;
	int Mstate;
	int Area;
	bool f = false;
};
person p[10005];

int father[10005];

int findfather(int x){
	if(x == father[x]){
		return x;
	}else{
		int F = findfather(father[x]);
		father[x] = F;
		return F;	
	}
}

void Union(int a,int b){
	int fa = findfather(a);
	int fb = findfather(b);
	if(fa!=fb){
		if(fa<fb){
			father[fb] = fa;
		}else{
			father[fa] = fb;	
		}
	}
}

int num[10005];
double area[10005];
double state[10005];


struct ans{
	int ID;
	int number;
	double state;
	double area;
};
ans a[10005];

bool vis[10005];
bool hashT[10005];

int cnt = 0;

bool cmp(ans a1,ans a2){
	if(a1.area!=a2.area){
		return a1.area > a2.area;
	}else{
		return a1.ID < a2.ID;	
	}
}

int main(){
	for(int i=0;i<=9999;i++){
		father[i] = i;
	}
	int N;
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		int id;
		scanf("%d",&id);
		hashT[id] = true;
		scanf("%d%d",&p[id].father,&p[id].mother);
		if(p[id].father!=-1){
			hashT[p[id].father] = true;
			Union(id,p[id].father);
		}
		if(p[id].mother!=-1){
			hashT[p[id].mother] = true;
			Union(id,p[id].mother);
		}
		int k;
		scanf("%d",&k);
		for(int j=0;j<k;j++){
			int cid;
			scanf("%d",&cid);
			p[id].child.push_back(cid);
			hashT[cid] = true;
			Union(id,cid);
		}
		scanf("%d%d",&p[id].Mstate,&p[id].Area);
		p[id].f = true;
	}
	
	
	for(int i=0;i<=9999;i++){
		int F = findfather(i);
		num[F]++;
		if(p[i].f){
			area[F] += p[i].Area;
			state[F] += p[i].Mstate;
		}
	}
	
	for(int i=0;i<=9999;i++){
		if(hashT[i]){
			int F = findfather(i);
			if(!vis[F] && num[F]){
				vis[F] = true;
				a[cnt].ID = i;
				a[cnt].number = num[F];
				a[cnt].state = state[F]/(num[F]*1.0);
				a[cnt].area = area[F]/(num[F]*1.0);
				cnt++;
			}	
		}
	}
	
	sort(a,a+cnt,cmp);
	printf("%d\n",cnt);
	for(int i=0;i<cnt;i++){
		printf("%04d %d %.3lf %.3lf\n",a[i].ID,a[i].number,a[i].state,a[i].area);
	}
	
	return 0;
} 

L2-008 最长对称子串 (25 分)

算法标签: DP
注意点: 动态规划求最长回文子串的题目,可得初始条件如下:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示从第i个字符到第j个字符为回文串,那么
d p [ i ] [ i ] = 1 , L = 1 dp[i][i]=1,L=1 dp[i][i]=1,L=1
d p [ i ] [ i + 1 ] = 1 , s [ i ] = = s [ i + 1 ] , L = 2 dp[i][i+1]=1,s[i]==s[i+1],L=2 dp[i][i+1]=1,s[i]==s[i+1],L=2
d p [ i ] [ j ] = 1 , d p [ i + 1 ] [ j − 1 ] = 1 且 s [ i ] = = s [ j ] , L > = 3 dp[i][j]=1,dp[i+1][j-1]=1且s[i]==s[j],L>=3 dp[i][j]=1,dp[i+1][j1]=1s[i]==s[j],L>=3

#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];

int main(){
	string s;
	getline(cin,s);
	int len = s.size();
	int ans = 1;
	for(int i=0;i<len;i++){
		dp[i][i] = 1;
		if(i>=1 && s[i-1]==s[i]){
			dp[i-1][i] = 1;
			ans = 2;
		}
	}
	
	for(int i=3;i<=len;i++){
		for(int j=0;j<len-i+1;j++){
			int k = j+i-1;
			if(s[k] == s[j] && dp[j+1][k-1]==1){
				dp[j][k] = 1;
				ans = i;
			}
		}
	}
	cout << ans << endl;
	return 0; 
}

L2-009 抢红包 (25 分)

算法标签: 排序

#include<bits/stdc++.h>
using namespace std;
struct node{
	double price = 0.0;
	int num = 0;
	int number;	
};
const int maxn = 1e4+5;
node stu[maxn];
bool cmp(node stu1,node stu2){
	if(fabs(stu1.price-stu2.price)>1e-3){
		return stu1.price>stu2.price;
	}else if(stu1.num!=stu2.num){
		return stu1.num>stu2.num;
	}else{
		return stu1.number<stu2.number;
	}
}
int main(){
	int N;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		stu[i].number = i;
		int K;
		scanf("%d",&K);
		for(int j=1;j<=K;j++){
			int id,v;
			scanf("%d%d",&id,&v);
			stu[i].price -= (v/100.0);
			stu[id].price += (v/100.0);
			stu[id].num++;
		}
	}
	sort(stu+1,stu+N+1,cmp);
	for(int i=1;i<=N;i++){
		printf("%d %.2lf\n",stu[i].number,stu[i].price);
	}
	return 0;
}

L2-010 排座位 (25 分)

算法标签: 并查集

#include<bits/stdc++.h>
using namespace std;
int N,M,K;
int father[105];
int findfather(int x){
	if(x==father[x]){
		return x;
	}else{
		int F = findfather(father[x]);
		father[x] = F;
		return F;	
	}
}
void Union(int a,int b){
	int fA = findfather(a);
	int fB = findfather(b);
	if(fA!=fB){
		father[fA] = fB;
	}
}

set<int> s2[105];
int main(){
	scanf("%d%d%d",&N,&M,&K);
	for(int i=1;i<=N;i++){
		father[i] = i;
	}
	for(int i=1;i<=M;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		if(w==1){
			Union(u,v);
		}else{
			s2[u].insert(v);
			s2[v].insert(u);
		}
	}
	for(int i=1;i<=K;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		int fu = findfather(u);
		int fv = findfather(v);
		if(fu==fv && s2[u].find(v)==s2[u].end()){
			printf("No problem\n");
		}else if(fu!=fv && s2[u].find(v)==s2[u].end()){
			printf("OK\n");
		}else{
			if(fu!=fv && s2[u].find(v)!=s2[u].end()){
				printf("No way\n");
			}else{
				printf("OK but...\n");
			}
		}
	}
	
	return 0;
}

L2-011 玩转二叉树 (25 分)

算法标签: 树,二叉树 + 遍历
注意点: 根据中序遍历和先序遍历建树,然后层序遍历即可

#include<bits/stdc++.h>
using namespace std;
int in[35];
int pre[35];

struct node{
	int data;
	node* lchild;
	node* rchild;
};

node* newnode(int v){
	node* Node = new node;
	Node->data = v;
	Node->lchild = NULL;
	Node->rchild = NULL;
	return Node;
}

node* Create(int preL,int preR,int inL,int inR){
	if(preL>preR){
		return NULL;
	}
	node* root = newnode(pre[preL]);
	int k;
	for(int i=inL;i<=inR;i++){
		if(pre[preL] == in[i]){
			k = i;
			break;
		}
	}
	int numleft = k-inL;
	root->lchild = Create(preL+1,preL+numleft,inL,inL+numleft-1);
	root->rchild = Create(preL+numleft+1,preR,inL+numleft+1,inR);
	return root;
}
void levelorder(node* root){
	queue<node*> q;
	q.push(root);
	bool f1 = true;
	while(!q.empty()){
		node* f = q.front();
		q.pop();
		if(f1){
			cout << f->data;
			f1 = false;
		}else{
			cout << " " << f->data;
		}
		if(f->rchild){
			q.push(f->rchild);
		}
		if(f->lchild){
			q.push(f->lchild);
		}
	} 
}
int main(){
	int n;
	cin >> n;
	for(int i=0;i<n;i++){
		cin >> in[i];
	}
	for(int i=0;i<n;i++){
		cin >> pre[i];
	}
	node* root = NULL;
	root = Create(0,n-1,0,n-1);
	levelorder(root);
	
	return 0;
} 

L2-012 关于堆的判断 (25 分)

算法标签: 堆 + 字符串
注意点: 本题考察堆的插入操作:
1.本题容易与Floyd建堆混淆,题目中明确要求逐个元素插入到堆中,因此,是采用自下而上的上滤算法建堆,而不是采用自下而上的下滤算法建堆
2.如何判断这四类操作
(1)根结点很显然,判断a[1]是否与输入的root相等
(2)判断兄弟结点,拥有共同的父节点,判断n1/2 == n2/2
(3)判断父节点,n2/2 == n1
(4)判断子节点,n1/2 == n2
3.字符串中数字的读入,这里有一个坑点,负数如-1003,需特判前面的负号[测试点1]/[测试点3]

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
int a[maxn];
void downadjust(int N){
	while(N>=2){
		if(a[N]<a[N/2]){
			swap(a[N],a[N/2]);
		}else{
			break;
		}
		N/=2;
	}
}
int main(){
	int N,M;
	cin >> N >> M;
	for(int i=1;i<=N;i++){
		cin >> a[i];
		downadjust(i);
	}
	getchar();
	for(int i=1;i<=M;i++){
		string s;
		getline(cin,s);
		if(s.find("is the root")!=-1){
			int site = s.find(" is the root");
			int num = 0;
			if(s[0] == '-'){
				for(int i=1;i<site;i++){
					num = num*10 + s[i] - '0';
				}
				num = -num;
			}else{
				for(int i=0;i<site;i++){
					num = num*10 + s[i] - '0';
				}	
			}
			if(a[1] == num){
				printf("T\n");
			}else{
				printf("F\n");
			}
		}else if(s.find("are siblings")!=-1){
			int site1 = s.find(" and");
			int num1 = 0;
			if(s[0] == '-'){
				for(int i=1;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}
				num1 = -num1;
			}else{
				for(int i=0;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}	
			}
			int site2 = s.find("and ");
			int site3 = s.find(" are");
			int num2 = 0;
			if(s[site2+4] == '-'){
				for(int i=site2+5;i<site3;i++){
					num2 = num2*10 + s[i] - '0';
				}
				num2 = -num2;
			}else{
				for(int i=site2+4;i<site3;i++){
					num2 = num2*10 + s[i] - '0';
				}	
			}
			int ans1,ans2;
			for(int i=1;i<=N;i++){
				if(num1 == a[i]){
					ans1 = i;
					break;
				}
			}
			for(int i=1;i<=N;i++){
				if(num2 == a[i]){
					ans2 = i;
					break;
				}
			}
			if(ans1/2 == ans2/2){
				printf("T\n");
			}else{
				printf("F\n");
			}
		}else if(s.find("is the parent of")!=-1){
			int site1 = s.find(" is");
			int num1 = 0;
			if(s[0] == '-'){
				for(int i=1;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}
				num1 = -num1;
			}else{
				for(int i=0;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}	
			}
			int site2 = s.find("of ");
			int num2 = 0;
			if(s[site2+3] == '-'){
				for(int i=site2+4;i<s.size();i++){
					num2 = num2*10 + s[i] - '0';
				}
				num2 = -num2;
			}else{
				for(int i=site2+3;i<s.size();i++){
					num2 = num2*10 + s[i] - '0';
				}	
			}
			int ans1,ans2;
			for(int i=1;i<=N;i++){
				if(num1 == a[i]){
					ans1 = i;
					break;
				}
			}
			for(int i=1;i<=N;i++){
				if(num2 == a[i]){
					ans2 = i;
					break;
				}
			}
			if(ans2/2==ans1){
				printf("T\n");
			}else{
				printf("F\n");
			}
		}else{
			int site1 = s.find(" is");
			int num1 = 0;
			if(s[0] == '-'){
				for(int i=1;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}
				num1 = -num1;
			}else{
				for(int i=0;i<site1;i++){
					num1 = num1*10 + s[i] - '0';
				}
			}
			int site2 = s.find("of ");
			int num2 = 0;
			if(s[site2+3] == '-'){
				for(int i=site2+4;i<s.size();i++){
					num2 = num2*10 + s[i] - '0';
				}
				num2 = -num2;
			}else{
				for(int i=site2+3;i<s.size();i++){
					num2 = num2*10 + s[i] - '0';
				}	
			}
			int ans1,ans2;
			for(int i=1;i<=N;i++){
				if(num1 == a[i]){
					ans1 = i;
					break;
				}
			}
			for(int i=1;i<=N;i++){
				if(num2 == a[i]){
					ans2 = i;
					break;
				}
			}
			if(ans1/2==ans2){
				printf("T\n"); 
			}else{
				printf("F\n");
			}
		}
	}
	
	return 0;
} 

L2-013 红色警报 (25 分)

算法标签: 并查集
注意点: 本题考察的算法是并查集,但考察的是并查集的一种变式:如何理解题目所给的条件(失去一个城市并不改变其他城市之间的连通性)
首先我们来分析一下这个条件:
(1)若失去一个城市之后。连通图数量减少了1:那么失去的城市本身就是一个连通分量,它并不影响其他城市的连通性
(2)若失去一个城市之后,连通图数量不变:此时意味着本来是若干个连通分量,删除结点后,仍然是若干个连通分量,这些连通分量中的城市互相可达,说明删除这个城市并不影响其他城市的连通性
(3)若失去一个城市之后,连通图数量增加了1:此时意味着多出来的连通分量必定是原连通分量裂开后形成的,此时破坏了其余城市之间的连通性,因此这种情况下会发出alert

#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
bool hashT[maxn];
vector<int> G[maxn];
int a[maxn];
int father[maxn];
vector<int> res;
int findfather(int x){
	if(x==father[x]){
		return x;
	}else{
		int F = findfather(father[x]);
		father[x] = F;
		return F;	
	}
}
void Union(int a,int b){
	int fA = findfather(a);
	int fB = findfather(b);
	if(fA!=fB){
		father[fA] = fB;
	}
}
int calcu(int N){
	set<int> s;
	for(int i=0;i<N;i++){
		father[i] = i;
	}
	for(int i=0;i<N;i++){
		if(!hashT[i]){
			for(int j=0;j<G[i].size();j++){
				int v = G[i][j];
				if(!hashT[v]){
					Union(i,v);
				}
			} 
		}
	}
	for(int i=0;i<N;i++){
		if(!hashT[i]){
			s.insert(findfather(i));
		}
	} 
	int ans = s.size();
	return ans; 
}
int main(){
	int N,M;
	cin >> N >> M;
	
	for(int i=1;i<=M;i++){
		int u,v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u); 
	}
	int K;
	cin >> K;
	for(int i=1;i<=K;i++){
		cin >> a[i];
	} 
	int start = calcu(N);
	for(int i=1;i<=K;i++){
		hashT[a[i]] = true;
		int tmp = calcu(N);
		if(tmp<=start){
			printf("City %d is lost.\n",a[i]);
		}else{
			printf("Red Alert: City %d is lost!\n",a[i]);
		}
		start = tmp;
	}
	if(K==N){
		printf("Game Over.\n");
	}
	
	return 0;
} 

L2-014 列车调度 (25 分)

算法标签: 单调队列
注意点: 一道好题!这道题思维难度比较大,但是代码量相对较少
首先我们分析一下题目,题目中要求火车出站的序列是单调递减的,也就是说编号较大的火车应该停靠在站台的前端,而编号较小的火车应该停靠在站台的后端,等到编号大的火车出站之后,编号小的自然排在了队头,这样即可满足出站的要求
仔细回想一下所学过的数据结构,发现单调队列正好满足这个要求,队列中总是保存着较小的编号,因此第一种方法就形成了,建立若干个单调队列,每个队列记录最小的编号,然后每次新来的火车,通过二分查找,找到最小编号最接近的站台,排在其后面,并更新该单调队列的记录,此方法可行,但还有更好的方案
纵观上一个方法,尽管我们需要维护若干个单调队列,但是,单调队列无需保存所有火车的编号,而只需要保存其中序列最小的那个即可,因此,可把每个队列抽象成一个数,这个数代表了能够进站的火车最大编号,所有小于这个数的火车被允许进站,排在其位置之后,并更新编号,编号的变化具有单调性(递减),而二分查找可替换成平衡树上查找,因此,我们引入一个集合set,用lower_bound二分查找,用insert快速插入,效率最高!

#include<bits/stdc++.h>
using namespace std;
set<int> s;
int main(){
	int N;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		int t;
		scanf("%d",&t);
		set<int>::iterator it;
		it = s.lower_bound(t);
		if(it!=s.end()){
			s.erase(it);
			s.insert(t);
		}else{
			s.insert(t);
		}
	}
	int ans = s.size();
	cout << ans << endl;
	return 0;
} 

L2-015 互评成绩 (25 分)

算法标签: 排序
注意点: 先对每个人的部分成绩进行排序,然后去掉最高分和最低分,取平均值计算,得到个人最终成绩,最后对所有人的成绩进行排序,输出前M个即可

#include<bits/stdc++.h>
using namespace std;
double ans[10005];
int a[15];
int main(){
	int N,K,M;
	scanf("%d%d%d",&N,&K,&M);
	for(int i=1;i<=N;i++){
		for(int j=1;j<=K;j++){
			scanf("%d",&a[j]);
		} 
		sort(a+1,a+1+K);
		double ave = 0.0;
		for(int j=2;j<=K-1;j++){
			ave += a[j];
		}
		ave /= (K-2);
		ans[i] = ave;
	}
	sort(ans+1,ans+1+N);
	for(int i=N-M+1;i<=N;i++){
		if(i!=N){
			printf("%.3lf ",ans[i]);
		}else{
			printf("%.3lf\n",ans[i]);
		}
	}
	return 0;
}

L2-016 愿天下有情人都是失散多年的兄妹 (25 分)

算法标签: 并查集 + BFS/DFS
注意点: 首先,考虑到这道题的数据结构不是一棵树,因为找不到公共的祖先源点(根结点不唯一,是森林),因此采用并查集将所有祖先关系都合并起来(祖先关系不同源的人无需判断5层)
接着,有K组输入(题目中未给出K的大小,实际测试结果来看,大约100组数据左右,因为若K很大,BFS/DFS都是超时的算法,需考虑倍增LCA),每组输入给出2个编号,首先判断2个编号的性别,如果一样,输出Never Mind,否则再判断是否同源,若不是,则输出Yes,若是,继续判断5代以内是否有交集,由于题目中说了,给的数据都是同辈份的,这里就省略了需将结点提升到同一层的步骤(倍增LCA),可直接将ID1编号用BFS算法遍历5个层次,把每个层次遍历过的结点都打上已访问的标签,再进行ID2编号,若在5个层次内访问到了已打过标签的结点,则输出No,否则输出Yes

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int hashT[maxn];
int father[maxn];
vector<int> G[maxn];
bool vis[maxn];

int findfather(int x){
	if(x==father[x]){
		return x;
	}else{
		int F = findfather(father[x]);
		father[x] = F;
		return F;	
	}
}
void Union(int a,int b){
	int fA = findfather(a);
	int fB = findfather(b);
	if(fA!=fB){
		father[fA] = fB;
	}
}
void BFS(int u){
	queue<pair<int,int> > q;
	q.push(make_pair(u,1));
	vis[u] = true;
	while(!q.empty()){
		pair<int,int> f = q.front();
		q.pop();
		int u = f.first;
		int layer = f.second;
		if(layer<5){
			for(int i=0;i<G[u].size();i++){
				int v = G[u][i];
				if(!vis[v]){
					vis[v] = true;
					q.push(make_pair(v,layer+1));
				}
			}
		}
	}
}
int Check(int u){
	queue<pair<int,int> > q;
	q.push(make_pair(u,1));
	vis[u] = true;
	while(!q.empty()){
		pair<int,int> f = q.front();
		q.pop(); 
		int u = f.first;
		int layer = f.second;
		if(layer<5){
			for(int i=0;i<G[u].size();i++){
				int v = G[u][i];
				if(!vis[v]){
					vis[v] = true;
					q.push(make_pair(v,layer+1));
				}else{
					return 0;
				} 
			}
		}
	}
	return 1;
} 
int main(){
	for(int i=0;i<maxn;i++){
		father[i] = i;
	}
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int id;
		scanf("%d",&id);
		char sex;
		cin >> sex;
		if(sex == 'M'){
			hashT[id] = 1;
		}else{
			hashT[id] = 2;
		}
		int mid,fid;
		scanf("%d%d",&fid,&mid);
		if(fid!=-1){
			G[id].push_back(fid);
			Union(id,fid);
			hashT[fid] = 1;
		}
		if(mid!=-1){
			G[id].push_back(mid);
			Union(id,mid);
			hashT[mid] = 2;
		}
	}
	int K;
	scanf("%d",&K);
	for(int i=1;i<=K;i++){
		int id1,id2;
		scanf("%d%d",&id1,&id2);
		if(hashT[id1] == hashT[id2]){
			printf("Never Mind\n"); 
		}else{
			int f1 = findfather(id1);
			int f2 = findfather(id2);
			if(f1 == f2){
				memset(vis,0,sizeof(vis));
				BFS(id1);
				int f = Check(id2);
				if(f == 1){
					printf("Yes\n");
				}else{
					printf("No\n");
				}
			}else{
				printf("Yes\n");
			}	
		}
	}
	return 0;
}

L2-017 人以群分 (25 分)

算法标签: 排序
注意点: 先对输入的序列进行排序,然后划分前N/2个为内向型集合,后N-N/2个为外向型集合,两个集合之差即为Diff

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int main(){
	int N;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		scanf("%d",&a[i]);
	}
	sort(a+1,a+N+1);
	int N1,N2;
	if(N%2){
		N1 = N/2 +1 ;
		N2 = N-N1;
	}else{
		N1 = N2 = N/2;
	}
	printf("Outgoing #: %d\n",N1);
	printf("Introverted #: %d\n",N2);
	int ans = 0;
	for(int i=N-N1+1;i<=N;i++){
		ans += a[i];
	}
	for(int i=1;i<=N2;i++){
		ans -= a[i];
	}
	printf("Diff = %d\n",ans);
	return 0;
}

L2-018 多项式A除以B (25 分)

算法标签: 高精
注意点: 本题的算法和高精除法一致,模拟竖式相除即可,坑点有如下几个:
(1)本题未标注数据范围,题目保证所有指数是各不相同的非负整数,所有系数是非零整数,所有整数在整型范围内。
这句话本身是有问题的,如果指数e无限大(int上界),无法存储多项式(空间溢出),如果系数无限大,每一轮乘除过后误差不断累积,最终会使得15位有效数字的double不再精确,当误差超过0.05时,那么也无法完成多项式相除的功能(输出结果错误)
事实上,本题的数据比较水,最大的数据指数<3000,项数<3000,因此,除法最多3000次就能完成,算法复杂度 O ( n 2 ) O(n^2) O(n2)即可
(2)零多项式是一个特殊多项式,对应输出为0 0 0.0,这个条件必须特判,测试点1/3/4会出现商或者余数是零多项式的情况
(3)舍入是什么:题目中说,多项式其实有常数项-1/27,但因其舍入后为0.0,这个例子还不够特殊,我们看输出样例:
3 2 0.3 1 0.2 0 -1.0,即
0.3 x 2 + 0.2 x − 1.0 0.3x^2+0.2x-1.0 0.3x2+0.2x1.0
我们用竖式除法计算得到,商是 1 3 x 2 + 2 9 x − 26 27 \frac{1}{3}x^2+\frac{2}{9}x-\frac{26}{27} 31x2+92x2726
− 26 27 = − 0.96 -\frac{26}{27}=-0.96 2726=0.96,答案输出为-1.0,这里就解释的很清楚了,舍入指的是对于小数点后1位进行四舍五入,因此用%.1lf的截断肯定错误,那么如何舍入呢?这里采用的是先将其扩大10倍,取整,四舍五入后,再除以10,还原成小数,对应于下面的convert函数

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3+5;
double a[maxn];
double b[maxn];
double c[maxn]; 
double convert(double s){
	s *= 10;
	int res = (round)(s);
	double ans = res*1.0/10.0;
	return ans;
}
int main(){
	int N;
	scanf("%d",&N);
	int max1 = -1;
	int max2 = -1;
	for(int i=1;i<=N;i++){
		int e,coff;
		scanf("%d%d",&e,&coff);
		a[e] = coff*1.0;
		max1 = max(max1,e);
	} 
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		int e,coff;
		scanf("%d%d",&e,&coff);
		b[e] = coff*1.0;
		max2 = max(max2,e);
	}
	for(int i=max1;i>=max2;i--){
		if(fabs(a[i])>1e-3){
			double t = a[i]/b[max2];
			c[i-max2] += t;
			for(int j=max2;j>=0;j--){
				if(fabs(b[j])>1e-3){
					a[i-max2+j] -= (t*b[j]);
				}
			}
		}
	}
	int ans1 = 0;
	for(int i=max1-max2;i>=0;i--){
		double t = convert(c[i]);
		if(fabs(t)>1e-2){
			ans1++;
		}
	}
	printf("%d",ans1);
	if(ans1){
		for(int i=max1-max2;i>=0;i--){
			double t = convert(c[i]);
			if(fabs(t)>1e-2){
				printf(" %d %.1lf",i,t);
			} 
		}	
	}else{
		printf(" 0 0.0");
	}
	
	printf("\n");
	int ans2 = 0;
	for(int i=max2-1;i>=0;i--){
		double t = convert(a[i]);
		if(fabs(t)>1e-2){
			ans2++;
		}
	}
	printf("%d",ans2);
	if(ans2){
		for(int i=max2-1;i>=0;i--){
			double t = convert(a[i]);
			if(fabs(t)>1e-2){
				printf(" %d %.1lf",i,t);
			}
		}	
	}else{
		printf(" 0 0.0");
	}
	
	return 0;
}

L2-019 悄悄关注 (25 分)

算法标签: 哈希
注意点: 先建立一个集合用于查询输入的名字,然后计算平均值,线性扫描并统计后按字典序排序输出

#include<bits/stdc++.h>
using namespace std;
set<string> s;
struct node{
	string name;
	int v;
};
node stu[10005];
string ans[10005];
int main(){
	int N;
	cin >> N;
	for(int i=1;i<=N;i++){
		string t;
		cin >> t;
		s.insert(t);
	}
	int M;
	cin >> M;
	double ave = 0.0;
	for(int i=1;i<=M;i++){
		cin >> stu[i].name >> stu[i].v;
		ave += stu[i].v;
	}
	ave /= M;

	int cnt = 0;
	for(int i=1;i<=M;i++){
		if(stu[i].v>ave && s.find(stu[i].name)==s.end()){
			ans[cnt++] = stu[i].name;
		}
	}
	sort(ans,ans+cnt);
	if(!cnt){
		cout << "Bing Mei You"  << endl;
	}else{
		for(int i=0;i<cnt;i++){
			cout << ans[i] << endl;
		}
	}
	return 0;
}

L2-020 功夫传人 (25 分)

算法标签: 树,二叉树 + 遍历
注意点: 本题考察树的层次遍历,需注意测试点1的特殊情况,即祖师爷也是得道者,初始化的功力应该加倍!

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
vector<int> G[maxn];
int hashT[maxn];
bool vis[maxn];
int N;
double Z,r;
double ans = 0.0;
void levelorder(int S){
	queue<pair<int,double>> q;
	if(hashT[S]){
		q.push(make_pair(S,Z*hashT[S]));
	}else{
		q.push(make_pair(S,Z));
	}
	vis[S] = true;
	while(!q.empty()){
		pair<int,double> f = q.front();
		q.pop();
		int u = f.first;
		double value = f.second;
		if(hashT[u]){
			ans += value;
		}
		for(int i=0;i<G[u].size();i++){
			int v = G[u][i];
			if(!vis[v]){
				if(hashT[v]){
					double temp = (100.0-r)/100.0 * value;
					temp = 1.0*temp*hashT[v];
					q.push(make_pair(v,temp));
					vis[v] = true;
				}else{
					double temp = (100.0-r)/100.0 * value;
					q.push(make_pair(v,temp));
					vis[v] = true;
				}	
			}
		}
	}
	long long res = (long long)ans;
	cout << res << endl;
}
int main(){
	cin >> N >> Z >> r;
	for(int i=1;i<=N;i++){
		int K;
		cin >> K;
		if(K==0){
			int M;
			cin >> M;
			hashT[i-1] = M;
		}else{
			for(int j=1;j<=K;j++){
				int v;
				cin >> v;
				G[i-1].push_back(v);
			}
		}
	}
	
	levelorder(0);
	return 0;
}

L2-021 点赞狂魔 (25 分)

算法标签: 哈希 + 排序
注意点: 用哈希方法统计不同标签的个数,然后结构体排序输出结果

#include<bits/stdc++.h>
using namespace std;
struct node{
	string s;
	int sum = 0;
	double ave = 0.0;
};
node stu[105];
bool cmp(node stu1,node stu2){
	if(stu1.sum!=stu2.sum){
		return stu1.sum > stu2.sum;
	}else{
		return stu1.ave < stu2.ave;
	}
}
int main(){
	int N;
	cin >> N;
	for(int i=1;i<=N;i++){
		cin >> stu[i].s;
		int K;
		cin >> K;
		set<int> s;
		for(int j=1;j<=K;j++){
			int t;
			cin >> t;
			s.insert(t);
		}
		int cnt =  s.size();
		stu[i].sum = cnt;
		stu[i].ave = K*1.0/cnt;
	}
	sort(stu+1,stu+1+N,cmp);
	if(N<3){
		for(int i=1;i<=N;i++){
			if(i==1){
				cout << stu[i].s;
			}else{
				cout << " " << stu[i].s;
			}
		}
		for(int i=1;i<=3-N;i++){
			cout << " " << "-";
		}
	}else{
		for(int i=1;i<=3;i++){
			if(i==1){
				cout << stu[i].s; 
			}else{
				cout << " " << stu[i].s;
			}
		}	
	}
	
	return 0;
}

L2-022 重排链表 (25 分)

算法标签: 链表 + 模拟
注意点: 一道关于链表的模拟题,分奇偶交错排列即可,难度不大,注意测试点3,这种情况下给出N个结点,但可连接成链表的结点数少于N个,因此需重新统计真正的结点个数,然后再进行计算!

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int address[maxn];
int nex[maxn];
int value[maxn];
int ans_add[maxn];
int ans_next[maxn];
int ans_value[maxn];
int main(){
	int S;
	cin >> S;
	int N;
	cin >> N;
	for(int i=1;i<=N;i++){
		int add;
		cin >> add;
		cin >> value[add] >> nex[add];
	}
	int t = S;
	int NL = 0;
	while(t!=-1){
		NL++;
		t = nex[t];
	}
	int N0 = NL/2;
	int cnt = 1;
	t = S;
	while(t!=-1){
		if(cnt<=N0){
			ans_value[cnt*2] = value[t];
			ans_add[cnt*2] = t;
		}else{
			int temp = (cnt-N0);
			temp = temp*2-1;
			if(NL%2==0){
				temp = NL-temp;
			}else{
				temp = NL-temp+1;
			}
			ans_value[temp] = value[t];
			ans_add[temp] = t;
		}
		cnt++;
		t = nex[t];
	}
	for(int i=1;i<=NL-1;i++){
		ans_next[i] = ans_add[i+1];
	}
	ans_next[NL] = -1;
	for(int i=1;i<=NL-1;i++){
		printf("%05d %d %05d\n",ans_add[i],ans_value[i],ans_next[i]);
	}
	printf("%05d %d %d\n",ans_add[NL],ans_value[NL],-1);
	return 0;
}

L2-023 图着色问题 (25 分)

算法标签: 图论 + 染色
注意点: 图染色的判断问题,首先是要判断颜色的数目,题目要求用K种颜色进行染色,也就是说,必须使用K种颜色,大于或小于都是不允许的,然后用K种颜色染色,保证无向图中,任意一条边的两个结点都是不同的颜色,最后输出判断的结果

#include<bits/stdc++.h>
using namespace std;

vector<int> G[505];
int a[505];
int main(){
	int V,E,K;
	cin >> V >> E >> K;
	for(int i=1;i<=E;i++){
		int u,v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u); 
	}
	int N;
	cin >> N;
	for(int i=1;i<=N;i++){
		for(int j=1;j<=V;j++){
			cin >> a[j];
		}
		bool f = false;
		set<int> s;
		for(int j=1;j<=V;j++){
			s.insert(a[j]);
		}
		if(s.size()!=K){
			printf("No\n");
		}else{
			for(int u=1;u<=V;u++){
				for(int j=0;j<G[u].size();j++){
					int v = G[u][j];
					if(a[u]!=a[v]){
						continue;
					}else{
						f = true;
						break;
					}
				}
				if(f){
					break;
				}
			}
			if(f){
				printf("No\n");
			}else{
				printf("Yes\n");
			}
		}
	}
	return 0;
} 

L2-024 部落 (25 分)

算法标签: 并查集
注意点: 并查集模板题,具体可参考数据结构专题系列-并查集篇

#include<bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int father[maxn];
int a[maxn];
int findfather(int x){
	if(x==father[x]){
		return x;
	}else{
		int F = findfather(father[x]);
		father[x] = F;
		return F;	
	}
}
void Union(int a,int b){
	int fA = findfather(a);
	int fB = findfather(b);
	if(fA!=fB){
		father[fA] = fB;
	}
}
set<int> s;
int main(){
	int N;
	cin >> N;
	for(int i=1;i<=10000;i++){
		father[i] = i;
	}
	for(int i=1;i<=N;i++){
		int K;
		cin >> K;
		for(int j=1;j<=K;j++){
			cin >> a[j];
			s.insert(a[j]);
		}
		for(int j=2;j<=K;j++){
			Union(a[1],a[j]);
		}
	}
	set<int> s2;
	for(auto it=s.begin();it!=s.end();it++){
		int t = *it;
		s2.insert(findfather(t));
	}
	int ans1 = s.size();
	int ans2 = s2.size();
	cout << ans1 << " " << ans2 << endl;
	int Q;
	cin >> Q;
	for(int i=1;i<=Q;i++){
		int u,v;
		cin >> u >> v;
		int fx = findfather(u);
		int fy = findfather(v);
		if(fx!=fy){
			cout << "N" << endl;
		}else{
			cout << "Y" << endl;
		}
	}
	return 0;
} 

L2-025 分而治之 (25 分)

算法标签: 图论 + 枚举
注意点: 枚举每个未被占领的顶点,判断是否连通即可!此处的连通是指不与任何城市有边连通,即孤立的顶点

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
bool hashT[maxn];
vector<int> G[maxn];
int main(){
	int N,M;
	cin >> N >> M;
	for(int i=1;i<=M;i++){
		int u,v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	int K;
	cin >> K;
	for(int i=1;i<=K;i++){
		memset(hashT,0,sizeof(hashT));
		int NP;
		cin >> NP;
		for(int i=1;i<=NP;i++){
			int t;
			cin >> t;
			hashT[t] = true;
		}
		bool f = true;
		for(int i=1;i<=N;i++){
			if(hashT[i]){
				continue;
			}else{
				for(int j=0;j<G[i].size();j++){
					int v = G[i][j];
					if(!hashT[v]){
						f = false;
						break;
					}
				}
				if(!f){
					break;
				}
			}
		}
		if(f){
			cout << "YES" << endl;
		}else{
			cout << "NO" << endl;
		}
	}
		
	
	return 0;
}

L2-026 小字辈 (25 分)

算法标签: 树,二叉树 + 遍历
注意点: 本题考察树的层次遍历,求树中最深结点的深度,并输出最底层叶结点的值

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int layer[maxn];
bool vis[maxn];
vector<int> G[maxn];
void levelorder(int root){
	queue<int> q;
	vis[root] = true;
	q.push(root);
	while(!q.empty()){
		int f = q.front(); 
		q.pop();
		for(int i=0;i<G[f].size();i++){
			int v = G[f][i];
			if(!vis[v]){
				vis[v] = true;
				layer[v] = layer[f] + 1;
				q.push(v);
			}
		}
	}
}
int main(){
	int N;
	scanf("%d",&N);
	int root = 0;
	for(int i=1;i<=N;i++){
		int t;
		scanf("%d",&t);
		if(t==-1){
			root = i;
			layer[root] = 1;
		}else{
			G[t].push_back(i);
		}
	}
	levelorder(root);
	int maxm = -1;
	for(int i=1;i<=N;i++){
		maxm = max(maxm,layer[i]);
	}
	cout << maxm << endl;
	int cnt = 0;
	for(int i=1;i<=N;i++){
		if(layer[i] == maxm){
			if(!cnt){
				cout << i;
			}else{
				cout << " " << i;
			}
			cnt++;
		}
	}
	return 0;
} 

L2-027 名人堂与代金券 (25 分)

算法标签: 排序
注意点: 需注意输出排名前K名的成绩单,由于存在并列的可能性,因此输出不一定是K个人

#include<bits/stdc++.h>
using namespace std;
struct node{
	string s;
	int score;
	int rank;
};
const int maxn = 1e4+5;
node stu[maxn];
bool cmp(node stu1,node stu2){
	if(stu1.score!=stu2.score){
		return stu1.score>stu2.score;
	}else{
		return stu1.s < stu2.s;
	}
}
int main(){
	int N,G,K;
	cin >> N >> G >> K;
	int ans = 0;
	for(int i=1;i<=N;i++){
		cin >> stu[i].s >> stu[i].score;
		if(stu[i].score>=G){
			ans += 50;	
		}else if(stu[i].score>=60){
			ans += 20;
		} 
	}
	cout << ans << endl;
	sort(stu+1,stu+1+N,cmp);
	stu[1].rank = 1;
	for(int i=2;i<=N;i++){
		if(stu[i].score == stu[i-1].score){
			stu[i].rank = stu[i-1].rank;
		}else{
			stu[i].rank = i;
		}
	}
	for(int i=1;i<=N;i++){
		if(stu[i].rank<=K){
			cout << stu[i].rank << " " << stu[i].s << " " << stu[i].score << endl;
		}else{
			break;
		}
	}
	return 0;
}

L2-028 秀恩爱分得快 (25 分)

算法标签: 字符串 + 枚举
注意点: 做这道题之前,可以先看这道题:PAT甲级 1139 First Contact (30 分),具体可从[OJ题解系列 目录导航帖]获得
本题难度比那道题略小,坑点数量也少一些,由于可能出现+0与-0元素,-代表女生,+代表男生,因此读入的时候,用字符串读入,再转换成整数,输出时也注意+0与-0的输出
此外,输出A和B的条件为:当且仅当与A最亲密的异性是B,与B最亲密的异性是A,若两者其一不满足,那么就分别输出A最亲密的和B最亲密的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int hashT[maxn];
int a[maxn];
double G[maxn][maxn];
int main(){
	int N,M;
	scanf("%d%d",&N,&M);
	for(int i=1;i<=M;i++){
		int K;
		scanf("%d",&K);
		for(int j=1;j<=K;j++){
			char s[6];
			scanf("%s",s);
			int number = 0;
			if(s[0] == '-'){
				for(int i=1;i<strlen(s);i++){
					number = number*10 + s[i]-'0';
				}
				hashT[number] = 2;
			}else{
				for(int i=0;i<strlen(s);i++){
					number = number*10 + s[i] -'0';
				}
				hashT[number] = 1;
			}
			a[j] = number;
		}
		double T = 1.0/K;
		for(int j=1;j<=K-1;j++){
			for(int k=j+1;k<=K;k++){
				G[a[j]][a[k]] += T;
				G[a[k]][a[j]] += T;
			}
		}
	}
	string sA,sB;
	cin >> sA >> sB;
	int f1 = 0,f2 = 0;
	int A = 0,B = 0;
	if(sA[0]=='-'){
		f1 = 2;
		for(int i=1;i<sA.size();i++){
			A = A*10 + sA[i]-'0';
		}
	}else{
		f1 = 1;
		for(int i=0;i<sA.size();i++){
			A = A*10 + sA[i] - '0';
		}
	}
	if(sB[0]=='-'){
		f2 = 2;
		for(int i=1;i<sB.size();i++){
			B = B*10 + sB[i] - '0';
		}
	}else{
		f2 = 1;
		for(int i=0;i<sB.size();i++){
			B = B*10 + sB[i] - '0';
		}
	}
	
	double maxA = -1.0; 
	double maxB = -1.0;
	
	for(int i=0;i<N;i++){
		if(((f1==2 && hashT[i]==1) || (f1==1 && hashT[i]==2)) && G[A][i]>maxA){
			maxA = G[A][i];
		}
	}
	for(int i=0;i<N;i++){
		if(((f2==2 && hashT[i]==1) || (f2==1 && hashT[i]==2)) && G[B][i]>maxB){
			maxB = G[B][i];
		}
	}
	double ans = G[A][B];
	
	if((fabs(ans-maxA)<1e-5 && fabs(ans-maxB)<1e-5)){
		cout << sA << " " << sB << endl;
	}else{
		for(int i=0;i<N;i++){
			if((f1==2 && hashT[i]==1) && fabs(G[A][i]-maxA)<1e-4){
				if(A==0){
					printf("-0 %d\n",i);
				}else{
					printf("%d %d\n",-A,i);
				}
			}else if((f1==1 && hashT[i]==2) && fabs(G[A][i]-maxA)<1e-4){
				if(i==0){
					printf("%d -0\n",A);
				}else{
					printf("%d %d\n",A,-i);
				}	
			}
		}
		for(int i=0;i<N;i++){
			if((f2==2 && hashT[i]==1) && fabs(G[B][i]-maxB)<1e-4){
				if(B==0){
					printf("-0 %d\n",i);
				}else{
					printf("%d %d\n",-B,i);
				}
			}else if((f2==1 && hashT[i]==2) && fabs(G[B][i]-maxB)<1e-4){
				if(i==0){
					printf("%d -0\n",B);
				}else{
					printf("%d %d\n",B,-i);
				}
			}
		}
	}
	return 0;
}

L2-029 特立独行的幸福 (25 分)

算法标签: 数论 + 模拟
注意点: 按照题目所给的条件模拟即可
(1)注意进入无限循环的判断,当出现了重复元素后,就退出迭代
(2)注意判断数字的“独立性”,将每个数迭代后,到1结束之间的所有数,都用哈希表记录下,未被记录过的数字一定是满足“独立性”的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
int a[maxn];
bool hashT[maxn];
int ans[maxn];
int calcu(int N){
	int ans = 0;
	while(N){
		int u = N%10;
		ans += u*u;
		N /=10;
	}
	return ans;
}
bool isprime(int N){
	if(N<2){
		return 0;
	}
	for(int i=2;i<=sqrt(N);i++){
		if(N%i==0){
			return 0;
		}
	}
	return 1;
}
int main(){
	int A,B;
	cin >> A >> B;
	for(int i=A;i<=B;i++){
		int t = i;
		set<int> s;
		while(t!=1){
			t = calcu(t);
			if(s.find(t) != s.end()){
				break;
			}else{
				s.insert(t);
			}
		}
		if(t == 1){
			for(auto it=s.begin();it!=s.end();it++){
				int u = *it;
				hashT[u] = true;
			}
			ans[i] = s.size();
		}
	}
	int cnt = 0;
	for(int i=A;i<=B;i++){
		if(!hashT[i] && ans[i]){
			cnt++;
			if(isprime(i)){
				printf("%d %d\n",i,2*ans[i]);
			}else{
				printf("%d %d\n",i,ans[i]);
			}
		}
	}
	if(!cnt){
		cout << "SAD" << endl;
	} 
	
	return 0;
}

L2-030 冰岛人 (25 分)

算法标签: LCA + BFS/DFS
注意点: 一道很坑的题目!
本题存在很多细节以及理解上的误差(题面表述不清晰):
(1)维京人后裔是可以通过姓的后缀判断其性别的,其他人则是在姓的后面加 m 表示男性、f 表示女性。这句话表明带有m/f的后缀既有可能是维京人的祖先,也有可能是不是维京人。
(2)题目保证给出的每个维京家族的起源人都是男性。这句话表明只有m可能是维京人的祖先,f则不是维京人,因此,m/f都没有上一代,在树上无需建立边,而其余情况需建立从child到parent的边(代码中vis数组记录无需建立边的情况,[测试点2]/[测试点4])
(3)题目保证不存在两个人是同名的,这意味着用名做key进行哈希查找
(4)接下来看题面的输出条件:
对每一个查询,根据结果在一行内显示以下信息:
1.若两人为异性,且五代以内无公共祖先,则输出 Yes;
2.若两人为异性,但五代以内(不包括第五代)有公共祖先,则输出 No;
3.若两人为同性,则输出 Whatever;(这里直接判断性别即可,外层if条件判断)
4.若有一人不在名单内,则输出 NA。(任意一个名字哈希查找失败,外层if条件判断)
3、4条件容易判断,1、2条件表述模糊,题目还补充了这么一个条件:
所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。
也就是说,要先找到公共祖先,然后判断该公共祖先不在任一个人的五代以内,比方说,LCA比A高9层,比B高3层,还是在五代以内,应该输出No,此时,我们再回看条件1、2,发现有表述不严谨之处,所谓五代以内,是指层次较高的结点五代以内没有公共祖先,对于层次较低的结点,可以理解为N代内无公共祖先(不限制5层,[测试点3]/[测试点6])

#include<bits/stdc++.h>
using namespace std;
map<string,int> mp1;
map<int,string> mp2;
map<string,int> sex;
const int maxn = 2e5+5;
string C1[maxn];
string C2[maxn];
bool vis[maxn];
vector<int> G[maxn];

int cnt = 0;
int check(int u1,int u2){
	vector<int> v1;
	v1.push_back(u1);
	vector<int> v2;
	v2.push_back(u2); 
	int t1 = G[u1].size();
	while(t1){
		v1.push_back(G[u1][0]);
		u1 = G[u1][0];
		t1 = G[u1].size();
	}
	int t2 = G[u2].size();
	while(t2){
		v2.push_back(G[u2][0]);
		u2 = G[u2][0];
		t2 = G[u2].size();
	}
	if(v1.back() == v2.back()){
		int size1 = v1.size();
		int size2 = v2.size();
		while(size1>0 && size2>0 && v1[size1-1] == v2[size2-1]){
			size1--;
			size2--;
		}
		size1++;
		size2++;
		if(size1>=5 && size2>=5){
			return 1;
		}else{
			return 0;
		}
	}else{
		return 1;
	}
}

int main(){
	int N;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		string s2,s1;		//s2-name,s1-surname
		cin >> s2 >> s1;
		int len1 = s1.size();
		string first;
		if(mp1.find(s2)==mp1.end()){
			mp1[s2] = ++cnt;
			mp2[cnt] = s2;
		}
		if(s1[len1-1] == 'm'){
			first = s1.substr(0,len1-1);
			sex[s2] = 1;
			vis[mp1[s2]] = true;
		}else if(s1[len1-1] == 'f'){
			first = s1.substr(0,len1-1);
			sex[s2] = 2;
            vis[mp1[s2]] = true;
		}else if(s1.substr(len1-4) == "sson"){
			first = s1.substr(0,len1-4);
			sex[s2] = 1;
		}else if(s1.substr(len1-7) == "sdottir"){
			first = s1.substr(0,len1-7);
			sex[s2] = 2;
		}
		if(mp1.find(first)==mp1.end()){
			mp1[first] = ++cnt;
			mp2[cnt] = first;
		}
		C1[i] = s2;
		C2[i] = first;
	}
	for(int i=1;i<=N;i++){
		if(!vis[mp1[C1[i]]]){
			int u = mp1[C1[i]];
			int v = mp1[C2[i]];
			G[u].push_back(v);	
		}
	}
	
	int M;
	scanf("%d",&M);
	for(int i=1;i<=M;i++){
		string name1,surname1,name2,surname2;
		cin >> name1 >> surname1 >> name2 >> surname2;
		if(mp1.find(name1) == mp1.end() || mp1.find(name2) == mp1.end()){
			printf("NA\n");
		}else{
			int t1 = sex[name1];
			int t2 = sex[name2];
			if(t1 == t2){
				printf("Whatever\n");
			}else{
				int u1 = mp1[name1];
				int u2 = mp1[name2];
				int op = check(u1,u2);
				if(op){
					printf("Yes\n");
				}else{
					printf("No\n");
				}
			}
		}
	} 
	return 0;
}

L2-031 深入虎穴 (25 分)

算法标签: 树,二叉树 + 遍历
注意点: 本题考察的算法为求一棵树的最大深度,这里采用DFS方法记录

#include<bits/stdc++.h>
using namespace std;
int N;
const int maxn = 1e5+5;
vector<int> G[maxn];
bool hashT[maxn];
vector<int> temp;
int ans;
int maxm = -1;
void dfs(int root){
	temp.push_back(root);
	if(G[root].size()==0){
		int S = temp.size();
		if(S>maxm){
			maxm = S;
			ans = root;
		}
	}else{
		for(int i=0;i<G[root].size();i++){
			int v = G[root][i];
			dfs(v);
		}
	}
	temp.pop_back();
}
int main(){
	cin >> N;
	for(int i=1;i<=N;i++){
		int K;
		cin >> K;
		for(int j=0;j<K;j++){
			int t;
			cin >> t;
			G[i].push_back(t);
			hashT[t] = true;
		}
	}
	int root = -1;
	for(int i=1;i<=N;i++){
		if(!hashT[i]){
			root = i;
			break;
		}
	}
	dfs(root);
	cout << ans << endl;
	
	return 0;
}

L2-032 彩虹瓶 (25 分)

算法标签: 栈混洗
注意点: 本题考察的是栈结构,给你一个序列,并且限制栈的大小,要求判断该序列是否为栈混洗的序列
当入栈元素超过栈大小时,或者是栈内还存在元素(无法取出)时,都是不合法序列,输出NO,其余情况输出YES

#include<bits/stdc++.h>
using namespace std;
int a[1005];
int main(){
	int N,M,K;
	cin >> N >> M >> K;
	for(int i=1;i<=K;i++){
		for(int j=1;j<=N;j++){
			cin >> a[j];
		}
		stack<int> s;
		int cnt = 1;
		bool f = true;
		for(int j=1;j<=N;){
			if(a[j] == cnt){
				cnt++;
				j++;
			}else if(!s.empty() && cnt == s.top()){
				s.pop();
				cnt++;
			}else{
				s.push(a[j]);
				j++;
				if(s.size()>M){
					f = false;
					break;
				}
			}
		}
		
		while(!s.empty()){
			if(s.top() == cnt){
				cnt++;
				s.pop();
			}else{
				break;
			}
		}
		if(!s.empty()){
			f = false;
		}
		if(f){
			printf("YES\n");
		}else{
			printf("NO\n");
		}
	} 
	
	return 0;
}

L2-033 简单计算器 (25 分)

算法标签:
注意点: 栈算法模板题,考察了栈的符号算术功能

#include<bits/stdc++.h>
using namespace std;
int a[1005];
stack<int> s1;	
stack<char> s2;
int main(){
	int N;
	cin >> N;
	for(int i=1;i<=N;i++){
		int t;
		cin >> t;
		s1.push(t);
	}
	for(int i=1;i<=N-1;i++){
		char ch;
		cin >> ch;
		s2.push(ch);
	}
	while(!s2.empty()){
		int n1 = s1.top();
		s1.pop();
		int n2 = s1.top();
		s1.pop();
		char ch = s2.top();
		s2.pop();
		if(ch == '+'){
			int ans = n2 + n1;
			s1.push(ans);
		}else if(ch == '-'){
			int ans = n2 - n1;
			s1.push(ans);
		}else if(ch == '*'){
			int ans = n2 * n1;
			s1.push(ans);
		}else{
			if(n1==0){
				printf("ERROR: %d/0",n2);
				exit(0);
			}else{
				int ans = n2 / n1;
				s1.push(ans);
			}
		}
	}
	cout << s1.top() << endl;
	return 0;
}

L2-034 口罩发放 (25 分)

算法标签: 大模拟 + 排序
注意点: 一道坑题!
由于题意不清,因此理解上可能会有各式各样的问题,首先,梳理一下题目中的各种词汇:
(1)申请信息——每一个数据块给出一天的申请信息、申请信息的每一条记录为申请记录
(2)发放信息——程序输出的发放记录,按照提交时间第一优先级,申请记录顺序第二优先级排序
(3)状况信息——顺序按照申请记录中出现的顺序确定,不重不漏(合法记录指ID号18位,且是数字)
纵观题目的各个条件,我们发现(2)是需要排序的,而(3)是不需要排序的。因此先处理(3),再处理(2)
要确保信息的不重复,则必须采用哈希算法,由于ID号的唯一性,因此用ID作为关键字,建立与name的映射
最后注意一点,ID号必须为18位,且都是数字(不是字符)!

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e4+5;
struct node{
	string name;
	string ID;
	int op;
	int h;
	int m;
	int Day;
	int number;
};
node stu[1005];

int hashT[maxn];
bool vis[maxn];
string ans[maxn];
int res = 0;

bool cmp(node stu1,node stu2){
	if(stu1.h!=stu2.h){
		return stu1.h < stu2.h;
	}else if(stu1.m!=stu2.m){
		return stu1.m < stu2.m;
	}else{
		return stu1.number < stu2.number;
	}
}

map<string,int> mp1;
map<int,string> mp2;
map<string,string> mp3;

int check(string s){
	for(int i=0;i<s.size();i++){
		if(s[i]>='0' && s[i]<='9'){
			continue;
		}else{
			return 0;
		}
	} 
	return 1;
}
int main(){
	int D,P;
	scanf("%d%d",&D,&P);
	int cnt = 0;
	for(int i=0;i<maxn;i++){
		hashT[i] = -100;
	}
	for(int i=1;i<=D;i++){
		int T,S;
		scanf("%d%d",&T,&S);
		for(int j=1;j<=T;j++){
			cin >> stu[j].name >> stu[j].ID >> stu[j].op;
			scanf("%d:%d",&stu[j].h,&stu[j].m);
			stu[j].Day = i;
			stu[j].number = j;
			int len = stu[j].ID.size();
			if(len==18 && check(stu[j].ID)){
				if(mp1.find(stu[j].ID) == mp1.end()){
					mp1[stu[j].ID] = ++cnt;
					mp2[cnt] = stu[j].ID;
					mp3[stu[j].ID] = stu[j].name;
				}
				int id = mp1[stu[j].ID];
				if(stu[j].op == 1 && !vis[id]){
					vis[id] = true;
					ans[res++] = stu[j].ID;
				}	
			}
		}
		sort(stu+1,stu+1+T,cmp);
		for(int j=1;j<=T;j++){
			int len = stu[j].ID.size();
			if(len==18 && check(stu[j].ID)){
				if(mp1.find(stu[j].ID) == mp1.end()){
					mp1[stu[j].ID] = ++cnt;
					mp2[cnt] = stu[j].ID;
					mp3[stu[j].ID] = stu[j].name;
				}
				int id = mp1[stu[j].ID];
				int D1 = hashT[id];
				if(stu[j].Day-D1>P && S>0){
					S--;
					hashT[id] = stu[j].Day;
					cout << stu[j].name << " " << stu[j].ID << endl;
				}
			}
		}
	}
	for(int i=0;i<res;i++){
		cout << mp3[ans[i]] << " " << ans[i] << endl;
	}
	return 0;
}

L2-035 完全二叉树的层序遍历 (25 分)

算法标签: 树,二叉树 + 遍历
注意点: 一道好题!这里给出了一种新的建树方式,后序遍历建树(完全二叉树),按序输出即为层次遍历的值

#include<bits/stdc++.h>
using namespace std;
int post[35];
int N;
void postorder(int root){
	if(root>N){
		return;
	} 
	postorder(root * 2);
	postorder(root * 2 + 1);
	cin >> post[root];
}
int main(){
	cin >> N;
	postorder(1);
	for(int i=1;i<=N;i++){
		if(i==1){
			cout << post[i];
		}else{
			cout << " " << post[i];
		}
	}
	
	return 0;
}

L2-036 网红点打卡攻略 (25 分)

算法标签: 图论 + 哈密尔顿环路
注意点: 本题考察哈密尔顿环路问题,首先需要判断哈密尔顿环路是否满足(路径连通,且访问每个顶点仅一次),满足条件则统计,并记录最短路径和当前的方案编号

#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
int G[205][205];
int a[205];
bool hashT[205];
int main(){
	memset(G,0x3f,sizeof(G));
	int N,M;
	cin >> N >> M;
	for(int i=1;i<=M;i++){
		int u,v,w;
		cin >> u >> v >> w;
		G[u][v] = G[v][u] = w;
	} 
	int K;
	cin >> K;
	int cnt = 0;
	int maxm = 0x3f3f3f3f;
	int site = -1;
	for(int i=1;i<=K;i++){
		memset(hashT,0,sizeof(hashT));
		memset(a,0,sizeof(a));
		int n;
		cin >> n;
		for(int j=1;j<=n;j++){
			cin >> a[j];
			hashT[a[j]] = 1;
		}
		if(n!=N){
			continue;
		}else{
			bool f = true;
			for(int i=1;i<=N;i++){
				if(hashT[i]){
					continue;
				}else{
					f = false;
					break;
				}
			}
			if(f){
				int ans = 0;
				bool f2 = true;
				for(int i=1;i<=N+1;i++){
					if(G[a[i-1]][a[i]]!=0x3f3f3f3f){
						ans += G[a[i-1]][a[i]];
					}else{
						f2 = false;
						break;
					}
				}
				if(f2){
					cnt++;
					if(ans<maxm){
						maxm = ans;
						site = i;
					}
				}
			}
		}
	}
	cout << cnt << endl;
	cout << site << " " << maxm << endl;
	return 0;
}

L2-037 包装机 (25 分)

算法标签: 模拟
注意点: 按照题目操作要求模拟包装机的过程,注意栈和队列的判空!

#include<bits/stdc++.h>
using namespace std;
queue<char> q[105];
stack<int> s;
string out = "";
int main(){
	int N,M,Smax;
	scanf("%d%d%d",&N,&M,&Smax);
	for(int i=1;i<=N;i++){
		for(int j=1;j<=M;j++){
			char t;
			cin >> t;
			q[i].push(t);
		}
	}
	int op;
	int cnt = 0;
	while(scanf("%d",&op) && op!=-1){
		if(op == 0){
			if(!s.empty()){
				char t = s.top();
				s.pop();
				out += string(1,t);
				cnt--;
			}
		}else{
			if(!q[op].empty()){
				char f = q[op].front();
				q[op].pop();
				if(cnt == Smax){
					char t = s.top();
					s.pop();
					out += string(1,t);
					s.push(f);
				}else{
					s.push(f);
					cnt++;
				}
			}
		}
	}
	cout << out << endl;
	return 0;
}

L2-038 病毒溯源 (25 分

算法标签: 树,二叉树 + DFS遍历
注意点: 首先找到根结点root(不是任何子结点的结点一定是根结点),然后从根结点开始执行DFS遍历,即可求出最大深度,输出字典序较小的结果即可

#include<bits/stdc++.h>
using namespace std;
int N;
const int maxn = 1e4+5;
vector<int> G[maxn];
bool hashT[maxn];
vector<int> temp;
vector<int> ans; 
int maxm = -1;
void dfs(int root){
	temp.push_back(root);
	if(G[root].size()==0){
		int S = temp.size();
		if(S>maxm){
			maxm = S;
			ans = temp;
		}else if(S == maxm){
			if(temp<ans){
				ans = temp;
			}
		}
	}else{
		for(int i=0;i<G[root].size();i++){
			int v = G[root][i];
			dfs(v);
		}
	}
	temp.pop_back();
}
int main(){
	cin >> N;
	for(int i=0;i<N;i++){
		int K;
		cin >> K;
		for(int j=0;j<K;j++){
			int t;
			cin >> t;
			G[i].push_back(t);
			hashT[t] = true;
		}
	}
	int root = -1;
	for(int i=0;i<N;i++){
		if(!hashT[i]){
			root = i;
			break;
		}
	}
	dfs(root);
	cout << ans.size() << endl;
	for(int i=0;i<ans.size();i++){
		if(i==0){
			cout << ans[i];
		}else{
			cout << " " << ans[i];
		}
	}
	return 0;
}

L2-039 清点代码库 (25 分)

算法标签: 哈希 + 排序
注意点: 这道题考察哈希算法,我们需要将一串“输出值”,通过哈希函数映射成一个数,从而满足题目中的查询要求,这里采用STL的map容器实现
首先读入不同模块的输入,若该组输入在哈希表中出现过,那么我们只需要将该模块的计数器+1,若没有出现过,那么我们需要在哈希表中建立一个映射,并将模块计数器初始化为1,输入结束后,我们对结构体进行排序,得到最终的答案!

#include<bits/stdc++.h>
using namespace std;
vector<int> V[10005];
struct node{
	int number;
	int ans = 0;
};
node r[10005];

map<vector<int>,int> mp;
map<int,vector<int> > mp2;
bool cmp(node r1,node r2){
	if(r1.ans!=r2.ans){
		return r1.ans > r2.ans;
	}else{
		vector<int> v1;
		vector<int> v2;
		v1 = mp2[r1.number];
		v2 = mp2[r2.number];
		return v1 < v2;
	}
}
int main(){
	int N,M;
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++){
		for(int j=1;j<=M;j++){
			int t;
			scanf("%d",&t);
			V[i].push_back(t);
		}
	}
	int cnt = 0;
	for(int i=1;i<=N;i++){
		if(mp.find(V[i])==mp.end()){
			mp[V[i]] = ++cnt;
			mp2[cnt] = V[i];
			r[cnt].ans = 1;
			r[cnt].number = cnt;
		}else{
			r[mp[V[i]]].ans++;
		}
	}
	sort(r+1,r+1+cnt,cmp);
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;i++){
		printf("%d",r[i].ans);
		vector<int> v = mp2[r[i].number];
		for(int j=0;j<v.size();j++){
			printf(" %d",v[j]);
		}
		printf("\n");
	}
	return 0;
}

L2-040 哲哲打游戏 (25 分)

算法标签: 大模拟
注意点: 本题考察的算法是大模拟,按照题目的要求,模拟游戏的读档、存档操作,哈希数组用来存档,读取也从该数组中取出,注意本题的剧本从1开始

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int hashT[105];
vector<int> G[maxn];
int main(){
	int N,M;
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++){
		int K;
		scanf("%d",&K);
		for(int j=1;j<=K;j++){
			int t;
			scanf("%d",&t);
			G[i].push_back(t);
		} 
	}
	int start = 1;
	for(int i=1;i<=M;i++){
		int op,x;
		scanf("%d%d",&op,&x);
		if(op == 0){
			start = G[start][x-1];
		}else if(op == 1){
			hashT[x] = start;
			printf("%d\n",start);
		}else{
			start = hashT[x];
		}
	}
	printf("%d\n",start);
	return 0;
} 

L2-041 插松枝(25分)

算法标签: 模拟 + 队列/栈
注意点: 易知本题考察的是队列/栈,小盒子是栈,推送器是队列
根据题目的要求,我们可以将其分成2个环节处理:推送器上还有小松针/推送器上没有小松针:
(1)推送器上还有小松针:此时先检查盒子内是否有松针,若有,则按要求不超过K个且满足松针大小单调不递增性质依次pop,直至不能pop时,若记录的小松针个数为K个,那么直接输出,否则,依次检查推送器上的小松针,满足要求的加入,直至K个输出;不满足要求的加入小盒子,此时若小盒子已满,也直接输出,这里的检查是序号单调递增的,N个检查完之后进入(2)环节,即没有小松针在推送器上了
(2)推送器上没有小松针:此时检查小盒子内是否有小松针,若有,则按要求不超过K个且满足松针大小单调非递增性质依次pop,直至empty

#include<bits/stdc++.h>
using namespace std;
int a[1005];
int b[1005];
int cnt = 1;
stack<int> s;
int main(){
	int N,M,K;
	scanf("%d%d%d",&N,&M,&K);
	for(int i=1;i<=N;i++){
		scanf("%d",&a[i]);
	}
	while(cnt<=N){
		int count = 0;
		int Height = 999;
		while(!s.empty() && count < K){
			if(s.top()<=Height){
				Height = s.top();
				b[++count] = s.top();
				s.pop();
			}else{
				break;
			}
		}
		if(count == K){
			for(int i=1;i<=count;i++){
				if(i == 1){
					printf("%d",b[i]);
				}else{
					printf(" %d",b[i]);
				}
			}
			printf("\n");
		}else{
			while(cnt<=N && count<K){
				if(a[cnt]<=Height){
					b[++count] = a[cnt];
					Height = a[cnt];
					cnt++;
				}else{
					int size = s.size();
					if(size<M){
						s.push(a[cnt]);
						cnt++;
					}else{
						break;
					}
				}
			}
			for(int i=1;i<=count;i++){
				if(i == 1){
					printf("%d",b[i]);
				}else{
					printf(" %d",b[i]);
				}
			}
			printf("\n");
		}
	}
	while(!s.empty()){
		int count = 0;
		int Height = 999; 
		while(!s.empty() && count<K && s.top()<=Height){
			b[++count] = s.top();
			Height = s.top();
			s.pop();
		}
		for(int i=1;i<=count;i++){
			if(i == 1){
				printf("%d",b[i]);
			}else{
				printf(" %d",b[i]);
			}
		}
		printf("\n");		
	}
	return 0;
} 

L2-042 老板的作息表(25分)

算法标签: 排序

#include<bits/stdc++.h>
using namespace std;
struct node{
	int start;
	int end;
};
const int maxn = 1e5+5;
node t[maxn];
bool cmp(node t1,node t2){
	if(t1.start!=t2.start){
		return t1.start < t2.start;
	}else{
		return t1.end < t2.end;
	}
}
int time_to_int(int h,int m,int s){
	return 3600*h + 60*m + s;
}
void int_to_time(int t1,int t2){
	int h1 = t1/3600;
	int m1 = t1%3600/60;
	int s1 = t1%60;
	int h2 = t2/3600;
	int m2 = t2%3600/60;
	int s2 = t2%60;
	printf("%02d:%02d:%02d - %02d:%02d:%02d\n",h1,m1,s1,h2,m2,s2);
}
int main(){
	int N;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		int h1,h2,m1,m2,s1,s2;
		scanf("%d:%d:%d - %d:%d:%d",&h1,&m1,&s1,&h2,&m2,&s2);
		t[i].start = time_to_int(h1,m1,s1);
		t[i].end = time_to_int(h2,m2,s2);
	}
	sort(t+1,t+1+N,cmp);
	int begin = 0;
	for(int i=1;i<=N;i++){
		if(t[i].start>begin){
			int_to_time(begin,t[i].start);
			begin = t[i].end;
		}else{
			begin = t[i].end;
		}
	}
	if(begin!=86399){
		int_to_time(begin,86399);
	}
	return 0;
}

L2-043 龙龙送外卖(25分)

L2-044 大众情人(25分)

算法标签: 图论 + Floyd
注意点: Floyd模板题,算法细节具体可详见数据结构专题系列——最短路
本题需仔细理解这句话:我们记一个人 i i i在一个异性 j j j眼中的距离感为 D i j ; D_{ ij}; Dij; i i i的“异性缘”定义为 1 m a x j ∈ S ( i ) ( D i j ​ ) \frac{1}{max_{j∈S(i)}{(D_{ij}​ )}} maxjS(i)(Dij)1,其中 S ( i ) S(i) S(i)是相对于 i i i的所有异性的集合。那么“大众情人”就是异性缘最好(值最大)的那个人。
要使得值最大,那么就是求 m a x ( D i j ) max(D_{ij}) max(Dij)最小的那组结果;
相对于 i i i的所有异性的集合,这意味着我们比较的是j眼中的i尽可能小,用数据结构语言描述为dp[j][i]最小(注意不是dp[i][j]

#include<bits/stdc++.h>
using namespace std;
int hashT[505];
int dp[505][505];
int res[505];

int main(){
	int N,M;
	scanf("%d%d",&N,&M);
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=N;i++){
		dp[i][i] = 0;
	}
	for(int i=1;i<=N;i++){
		char sex;
		cin >> sex;
		if(sex == 'F'){
			hashT[i] = 2;
		}else{
			hashT[i] = 1;
		}
		int K;
		scanf("%d",&K);
		for(int j=1;j<=K;j++){
			int num,dis;
			scanf("%d:%d",&num,&dis);
			dp[i][num] = min(dp[i][num],dis);
		}
	}
	for(int k=1;k<=N;k++){
		for(int i=1;i<=N;i++){
			for(int j=1;j<=N;j++){
				dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);	
			}
		}	
	}
	int maxm = INT_MAX;
	for(int i=1;i<=N;i++){
		if(hashT[i] == 2){
			int maxt = -1;
			for(int j=1;j<=N;j++){
				if(hashT[j] == 1){
					maxt = max(maxt,dp[j][i]);
				}
			}
			maxm = min(maxm,maxt);
		}
	}
	int cnt = 0;
	for(int i=1;i<=N;i++){
		if(hashT[i] == 2){
			int maxt = -1;
			for(int j=1;j<=N;j++){
				if(hashT[j] == 1){
					maxt = max(maxt,dp[j][i]); 
				}
			}
			if(maxt == maxm){
				if(!cnt){
					printf("%d",i);
				}else{
					printf(" %d",i);
				}
				cnt++;
			}
		} 
	}
	printf("\n");
	maxm = INT_MAX;
	cnt = 0;
	for(int i=1;i<=N;i++){
		if(hashT[i] == 1){
			int maxt = -1;
			for(int j=1;j<=N;j++){
				if(hashT[j] == 2){
					maxt = max(maxt,dp[j][i]);
				}
			}
			maxm = min(maxm,maxt);
		}
	}
	for(int i=1;i<=N;i++){
		if(hashT[i] == 1){
			int maxt = -1;
			for(int j=1;j<=N;j++){
				if(hashT[j] == 2){
					maxt = max(maxt,dp[j][i]); 
				}
			}
			if(maxt == maxm){
				if(!cnt){
					printf("%d",i);
				}else{
					printf(" %d",i);
				}
				cnt++;
			}
		} 
	}
	printf("\n");
	return 0;
}
  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值