PAT甲级(1124-1155)

题目列表知识点
1124 Raffle for Weibo Followers (20 分)简单模拟
1125 Chain the Ropes (25 分)贪心,思维
1126 Eulerian Path (25 分)DFS,欧拉图
1127 ZigZagging on a Tree (30 分)中序+后序=>层序,逐层输出
1128 N Queens Puzzle (20 分)n皇后问题
1129 Recommendation System (25 分)set,结构体小于号重载
1130 Infix Expression (25 分)DFS中序遍历
1131 Subway Map (30 分)DFS
1132 Cut Integer (20 分)字符串处理
1133 Splitting A Linked List链表模拟
1134 Vertex Cover (25 分)顶点覆盖集,遍历边
1135 Is It A Red-Black Tree (30 分)红黑树,DFS
1136 A Delayed Palindrome (20 分)模拟加法,回文数判断
1137 Final Grading (25 分)结构体排序
1138 Postorder Traversal (25 分)先序+中序=>后序
1139 First Contact (30 分)图,模拟
1140 Look-and-say Sequence (20 分)简单模拟
1141 PAT Ranking of Institutions (25 分)结构体排序,精度问题
1142 Maximal Clique (25 分)遍历图
1143 Lowest Common Ancestor (30 分)利用先序和中序求最近公共祖先
1144 The Missing Number (20 分)暴力
1145 Hashing - Average Search Time (25 分)哈希查找,二次探测处理冲突
1146 Topological Order (25 分)拓扑排序
1147 Heaps (30 分)大顶堆,层序=>后序
1148 Werewolf - Simple Version (20 分)思维,枚举
1149 Dangerous Goods Packaging (25 分)简单题
1150 Travelling Salesman Problem (25 分)旅行商问题,图的遍历
1151 LCA in a Binary Tree (30 分)利用先序和中序求最近公共祖先
1152 Google Recruitment (20 分)字符串处理,质数判断
1153 Decode Registration Card of PAT (25 分)结构体排序,前缀零
1154 Vertex Coloring (25 分)遍历图
1155 Heap Paths (30 分)DFS输出简单路径,判断大/小顶堆

1124 Raffle for Weibo Followers (20 分)

题目大意:

给定一列人的名字,在这些人中进行抽奖,第一个中奖的人是第N个,每隔S个人抽一次奖,如果某人已经被抽中过了,则跳过他,选择下一个没被抽中过的人。

简单模拟,不过要注意,有可能人数不够,导致没有中奖的人。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const int INF=0x3f3f3f3f;
map<string,int>mp;
string t[maxn];
int main()
{
	int n,m,s;
	cin>>n>>m>>s;
	for(int i=1;i<=n;i++){
		cin>>t[i];
	}
	int flag=0;
	for(int i=s;i<=n;){
		if(mp[t[i]]==0){
			cout<<t[i]<<endl;
			flag=1;
			mp[t[i]]=1;
			i+=m;
		}
		if(mp[t[i]]==1){//这个人已经中过奖了,找下一个没有中过奖的
			i++;
		}
	}
	if(!flag) cout<<"Keep going...\n";//没有中奖的人
}

1125 Chain the Ropes (25 分)

题目大意:给出n段任意长度的绳子,需要把这n段绳子连在一起。将两段绳子连在一起之后,每段绳子的长度都会减半。求n段绳子连在一起之后可能长度的最大值是多少。

贪心,绳子每次和其他绳子相连,长度都会减半,越早相连,最终的长度就会越短。因此长度越长的绳子要越迟相连。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int a[maxn];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	sort(a,a+n);
	double ans=a[0];//如果只有一段绳子,那结果就是它自己,不用减半!
	for(int i=1;i<n;i++){
		ans=(ans+a[i])/2;
	}
	printf("%d\n",(int)ans);
	return 0;
}

1126 Eulerian Path (25 分)

题目大意:

给出一个无向图,判断其是否为欧拉图,伪-欧拉图或者不是欧拉图。

1、欧拉图:所有顶点的度数均为偶数的连通图

2、伪-欧拉图:有且仅有两个顶点的度数为奇数的连通图

一开始只是单纯计算了每个顶点的度数,忘了最重要的前提:

不管是欧拉图还是伪-欧拉图都首先必须是连通图!!!

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int deg[maxn];
vector<int>e[maxn];
int vis[maxn];
int sum;
int n,m;
void dfs(int x){//跑连通块
	if(x>n) return;
	vis[x]=1;
	for(int i=0;i<e[x].size();i++){
		if(!vis[e[x][i]]){
			dfs(e[x][i]);
		}
	}
	
}
int main(){
	cin>>n>>m;
	while(m--){
		int u,v;
		cin>>u>>v;
		e[u].pb(v);
		e[v].pb(u);
		deg[u]++;//统计顶点的度数
		deg[v]++;	
	}
	sum=0;
	int odd=0;
	int even=0;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			dfs(i);
			sum++;
		}
		cout<<deg[i];
		if(i==n) cout<<endl;
		else cout<<" ";
		if(deg[i]&1) odd++;
		else even++;
	}
	if(sum==1){//只有一个连通块,则说明是连通图
		if(even==n) puts("Eulerian");
		else if(odd==2) puts("Semi-Eulerian");
		else if(odd>2) puts("Non-Eulerian");
	}
	else puts("Non-Eulerian");
	
	
}

1127 ZigZagging on a Tree (30 分)

题目大意:

给出一棵二叉树的中序遍历和先序遍历,输出其zigzagging order:从根节点开始,逐层输出。上一层从左到右输出,下一层则从右到左输出。

同样由中序遍历和后序遍历可以求出层序遍历。

已知层序遍历,可以很容易知道每一层的节点,用一个vector记录每一层的所有节点,按照题目要求输出即可。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int post[maxn],in[maxn];
map<int,int>lev;
int n;
vector<int>v[maxn];//记录每层的节点
void level(int root,int l,int r,int idx){//中序、后序求层序
	if(l>r) return;
	int k=post[root];
	int j=0;
	while(j<n&&in[j]!=k) j++;
	lev[idx]=k;
	level(root-r+j-1,l,j-1,2*idx+1);
	level(root-1,j+1,r,2*idx+2);	
}
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>in[i];
	}
	for(int i=0;i<n;i++){
		cin>>post[i];
	}
	level(n-1,0,n-1,0);
	map<int,int>::reverse_iterator it=lev.rbegin();
	int l=it->fi;//层序遍历最大下标
	int h=0;
	v[h].pb(0);
	while(1){
		int flag=0;
		int t=h;
		for(int i=0;i<v[t].size();i++){//遍历第t层的节点,其子节点即为第t+1层的节点
			int root=v[t][i];
			int lc=2*root+1;
			int rc=2*root+2;
			if(lc<=l&&lev[lc]!=0){//如果左节点存在
				if(flag)
				v[h].pb(lc);
				else{
					flag=1;
					v[++h].pb(lc);
				}				
			}
			if(rc<=l&&lev[rc]!=0){//如果右节点存在
				if(flag)
				v[h].pb(rc);
				else{
					flag=1;
					v[++h].pb(rc);
				}				
			}
		}
		if(!flag) break;//所有节点均遍历完
	}
	int flag=0;
	cout<<lev[v[0][0]];
	for(int i=1;i<=h;i++){
		if(!flag){
			for(int j=0;j<v[i].size();j++){//从左到右输出
				cout<<" "<<lev[v[i][j]];
			}
			flag=1;
		}
		else{
			int m=v[i].size();
			for(int j=m-1;j>=0;j--){//从右到左输出
				cout<<" "<<lev[v[i][j]];
			}
			flag=0;
		}
	}
}

1128 N Queens Puzzle (20 分)

n皇后问题:同一行,同一列,同一对角线不会出现两个及以上的皇后。

给出每一列中皇后的行号序列,判断其是否满足n皇后问题。

题目给出每一列皇后的行号,也就意味着我们无需考虑同一列的情况。

一开始不太理解什么样才叫不在同一对角线,对着样例看了很久也没有理解正确。看了别人的文章,才知道:

​ 如果存在两个皇后,其位置的连线斜率等于1或-1,则说明在同一对角线上。

1、对于在同一行:存在两个皇后的行号之差为0(同一行号出现次数大于1)

2、对于在同一对角线:存在两个皇后的行号之差等于列号之差

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e4+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int a[maxn];
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		int flag=1;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<i;j++){
				if(abs(a[i]-a[j])==0||abs(a[i]-a[j])==abs(i-j)){
					flag=0;
					break;
				}
			}
		}
		if(flag) puts("YES");
		else puts("NO");
	}
	return 0;
}

1129 Recommendation System (25 分)

个性推荐系统可以预测客户对某一件商品的喜爱程度。现给出一个序列,代表某客户曾经浏览过的商品,你需要根据用户曾经浏览某商品的次数,进行商品推荐。

关于输出:
query: rec[1] rec[2] … rec[K]

query:当前用户正在浏览的商品,rec[1] rec[2] … rec[K]是系统为其推荐的商品,他们都是该客户曾经浏览次数最多的商品。如果存在两件商品浏览次数一样多,那么按照商品编号升序输出。

注意:如果用户当前浏览的是第一件商品,系统是无法为其进行推荐的,因为之前并没有产生过浏览记录,无从得知客户的喜好。

我的第一思维是每次遍历前面的商品,然后统计次数,而且还要进行排序,最后输出。很麻烦很麻烦,而且这题数据范围50,000,这样过于暴力,应该会超时,然后就卡死了。。。

看了柳婼大神的题解,真的学到了很多很多!

利用set的查找和删除函数以及自动排序的特性,可以很方便的更新某商品的浏览次数。

需要商品编号和浏览次数的一一对应,而且要同时插入set容器中,可以用一个结构体存储。(一开始想过用pair,但显然用pair达不到排序的要求)

由于还要进行排序,对结构体重载小于号,set自动排序的时候就会按找重载小于号的规则进行排序啦。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=5e4+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct node{
	int num;//商品编号
	int cnt;//商品浏览次数
	bool operator < (const node &a) const {//重载小于号
		return (cnt!=a.cnt) ? cnt>a.cnt:num<a.num;
	}
};
int book[maxn];
set<node>s;
int main(){
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++){
		int x;
		cin>>x;
		if(i!=0){
			cout<<x<<":";
			int cnt=0;
			for(auto it=s.begin();it!=s.end()&&cnt<k;it++){
				cout<<" "<<it->num;
				cnt++;
			}
			cout<<endl;
		}
		auto it=s.find(node{x,book[x]}); //查找当前set中是否存在商品x的信息
		if(it!=s.end()) s.erase(it);//删除
		book[x]++;//更新
		s.insert(node{x,book[x]});
	}
	return 0;
}

1130 Infix Expression (25 分)

题目大意:给出一棵二叉的语法树,输出其对应的中缀表达式。括号反应了操作符的优先级。

输入:data left_child right_child

data是第i个节点所代表的字符串,left_child是该节点左孩子的节点编号,right_child是该节点右孩子的节点编号。

大体思路:首先找出根节点—>根节点不是任何节点的儿子。然后中序遍历二叉树,同时在合适位置插入括号。

(其实感觉跟链表的题目有点像,遍历链表的时候也要先知道头节点,然后就能进行整个链表的遍历了,这里也是一样,知道了根节点是谁,就可以知道它的左右孩子,然后可以不断这样搜索下去,知道叶子节点)

中序遍历不是问题,关键是在哪里插入括号呢?

我们可以发现:对于每一棵子树来说,只有当前子树的根节点是运算符,而左右孩子都是运算符需要靠连接字符串。我们需要给这棵子树构成的“运算式”添加括号。注意子树不包括原来那棵二叉树,也就是说子树的根节点不可以是整棵树的根节点,那样的话会多加一对括号。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct tree{
	string data;
	int lc,rc;
}tr[maxn];
int vis[maxn];
int n,root;
string s;
void dfs(int idx){
	if(idx==-1){
		return;
	}
	int lc=tr[idx].lc;
	int rc=tr[idx].rc;
	if((tr[idx].lc!=-1||tr[idx].rc!=-1)&&idx!=root){//如果该节点不是叶子节点,(对于一颗最小的子树来说(小于等于三个节点),不是叶子节点的节点,就是子树的根节点)且不是整棵树的根节点,加括号
		s+="(";
	}
	dfs(lc);
	s+=tr[idx].data;
	dfs(rc);
	if((tr[idx].lc!=-1||tr[idx].rc!=-1)&&idx!=root){
		s+=")";
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>tr[i].data>>tr[i].lc>>tr[i].rc;
		if(tr[i].lc!=-1) vis[tr[i].lc]=1;
		if(tr[i].rc!=-1) vis[tr[i].rc]=1;
	}
	for(int i=1;i<=n;i++){//寻找根节点
		if(!vis[i]){
			root=i;
			break;
		}
	}
	dfs(root);//中序遍历
	cout<<s<<endl;
	return 0;
}

1131 Subway Map (30 分)

DFS

1132 Cut Integer (20 分)

题目大意:
将一个整数Z对半分分成A和B两部分,若Z能够整除A*B,输出Yes,否则输出No

考虑到数据范围,不超过20位的整数,故直接用字符串输入,然后用substr分段,再用stoi直接string转int
(这些函数就真的很好用)

注意:
A*B为0时,单独判断,否则会出现浮点错误。
除数为0–>浮点错误

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int main(){
	int n;
	cin>>n;
	while(n--){
		string s;
		cin>>s;
		int l=s.size();
		ll x=stoi(s);
		ll a=stoi(s.substr(0,l/2));
		ll b=stoi(s.substr(l/2,l/2));
        if(a*b==0) puts("No");
        else if(x%(a*b)==0) puts("Yes");
		else puts("No");
	} 
	return 0;
}

1133 Splitting A Linked List (25 分)

题目大意:
对给定的链表进行重排,规定负数排在最前面,然后是[0,k]内的数字,最后是大于k的数字。但是重排后链表中元素的相对顺序得跟原来在链表中的相对顺序需保持一致。

大体思路,遍历一遍原链表,将链表分为三个部分,然后按顺序输出
但要注意,可能出现某一部分为空的情况,此时就容易出错,特别是链表最后一个的next地址是-1,这个-1输出的位置。
所以为了避免后续的分类讨论,可以再将三个部分的元素依次push到同一个容器中,然后按顺序输出链表就好啦

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct node{
	int data;
	int add,next;
}q[maxn];
vector<int>neg,sma,lar;
int main(){
	int head,n,k;
	cin>>head>>n>>k;
	int des;
	for(int i=0;i<n;i++){
		int ad,num,nex;
		cin>>ad>>num>>nex;
		q[ad].add=ad;
		q[ad].data=num;
		q[ad].next=nex;
	}
	int p=head;
	while(p!=-1){//遍历链表
		if(q[p].data<0) neg.pb(p);
		if(q[p].data>=0&&q[p].data<=k) sma.pb(p);
		if(q[p].data>k) lar.pb(p);
		p=q[p].next;
	}
	for(int i=0;i<sma.size();i++){
		neg.pb(sma[i]);
	} 
	for(int i=0;i<lar.size();i++){
		neg.pb(lar[i]);
	} 
	int l=neg.size();
	for(int i=0;i<l-1;i++){
		printf("%05d %d %05d\n",neg[i],q[neg[i]].data,neg[i+1]);
	}
	printf("%05d %d -1\n",neg[l-1],q[neg[l-1]].data);
	return 0;
}

1134 Vertex Cover (25 分)

题目大意:
一幅图的顶点覆盖集(vertex cover)是这样定义的:
图中的每一条边的两个顶点至少有一个顶点在该顶点集中。
现给出一个顶点集,判断其是否为vertex cover

根据定义可以知道:如果存在这样一条边:它的两个顶点都不在这个顶点集中,那它肯定不是vertex cover。
(一开始绕来绕去想的太复杂,而且太暴力了,想清楚之后,发现十分简单)
标记一下所给顶点集中出现的所有顶点,然后遍历图中所有的边,如果出现边的两个顶点都没有出现在顶点集的情况,则说明顶点集不是vertex cover。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e4+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct edge{//存边
	int u,v;
}e[maxn];
int vis[maxn];  
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u,v;
		cin>>u>>v;
		e[i].u=u;
		e[i].v=v;
	}
	int q;
	cin>>q;
	while(q--){
		int k;
		cin>>k;
		memset(vis,0,sizeof(vis));
		for(int i=0;i<k;i++){
			int x;
			cin>>x;
			vis[x]=1;
		}
		int flag=1;
		for(int i=0;i<m;i++){
			if(!vis[e[i].u]&&!vis[e[i].v]){
				flag=0;
				break;
			}
		}
		if(flag) puts("Yes");
		else puts("No");
	}
	return 0;
}

1135 Is It A Red-Black Tree (30 分)

题目大意:
给出二叉搜索树的先序遍历,负号代表该节点涂上红色,否则为黑色。判断是否为红黑树。

根据题目所给红黑树的定义:
1、每个节点要么是黑色,要么是红色;
2、根节点是黑色;
3、空节点是黑色;
4、红色节点的所有子节点都是黑色;
5、对于任意节点,从该节点一直到其后代叶子节点的简单路径中经过的黑色节点数均相同;
一篇很有趣的介绍红黑树的文章
一开始,由于之前没接触过红黑树,于是对其定义产生了些许误解,误认为所有父节点都必须跟子节点颜色不同,其实并非如此。
红黑树只要求:红色节点的所有子节点都是黑色。而并没有要求黑色节点的所有子节点要是红色,也就是说黑色父节点可以存在黑色子节点。

二叉搜索树知道其先序,相当于也知道了中序,所以根据先序和中序求层序,然后根据父子的下标关系,进行颜色判断。

其次,在满足上述颜色方面的要求之后,还需判断第五个条件,跑一个dfs!(感觉自己对于dfs回溯的理解还是不够透彻,调试了半天,才调对,不过最终也完全靠自己过了这题,继续加油!嘿嘿),并利用set去重的特性,加以判断。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e4+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int pre[maxn],in[maxn];
int vis[maxn];  
map<int,int>mp;
set<int>s;
int n,m;
void level(int root,int l,int r,int idx){//由先序后中序求层序
	if(l>r) return;
	int k=pre[root];
	int j=l;
	while(j<r&&in[j]!=k) j++;
	level(root+1,l,j-1,idx*2+1);
	level(root+j-l+1,j+1,r,idx*2+2);
	mp[idx]=k;
}
void dfs(int root,int cnt){
	if(root>m){
		s.insert(cnt);
		return;
	}
	if(vis[mp[root]]==0&&mp[root]!=0) cnt++;
	int lc=2*root+1;
	int rc=2*root+2;
	dfs(lc,cnt);
	//if(vis[mp[lc]]==0&&lc<=m) cnt--;
	dfs(rc,cnt);
	//if(vis[mp[rc]]==0&&rc<=m) cnt--;	
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cin>>n;
		memset(vis,0,sizeof(vis));
        mp.clear();        
		for(int i=0;i<n;i++){
			int x;
			cin>>x;
			if(x<0){//红色节点
				x=-x;
				vis[x]=1;
			}
			pre[i]=x;
			in[i]=x;
		}
		sort(in,in+n);
		level(0,0,n-1,0);//求层序
		if(vis[mp[0]]){//根节点不是黑色
			puts("No");
			continue;
		}
		map<int,int>::reverse_iterator it=mp.rbegin();
		m=it->fi;//二叉树序号最多标到几
		int flag=1;
		for(int i=0;i<=m;i++){
			int lc=2*i+1;
			int rc=2*i+2; 
			if(mp[lc]!=0&&vis[mp[i]]==1&&vis[mp[lc]]==1){//父节点是红色,子节点也出现红色
				flag=0;
				break;
			}
			if(mp[rc]!=0&&vis[mp[i]]==1&&vis[mp[rc]]==1){
				flag=0;
				break;
			}
		}
		s.clear();//注意每次清空!
		if(flag) dfs(0,0);//如果所每条路径经过的黑色节点数相同,那么set里面只会出现一个数
//		for(auto i:s){
//			cout<<i<<endl;
//		}
		if(flag&&s.size()==1) puts("Yes");
		else puts("No");
	}
	return 0;
}

1136 A Delayed Palindrome (20 分)

此题需要注意数据范围:不超过1000位的正整数
所以肯定要用字符串或者数组来模拟加法啦
考虑到要判断是否为回文,用字符串更加方便

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const int INF=0x3f3f3f3f;
int check(string a){//判断回文
	string b=a;
	reverse(a.begin(),a.end());
	if(a==b) return 1;
	else return 0;
}
string add(string a,string b){//模拟加法
	string ans;
	int jw=0;
	for(int i=a.length()-1;i>=0;i--){
		int num=a[i]-'0'+b[i]-'0';
		if(jw){
			num+=jw;
			jw=0;
		}
		if(num>=10){
			num-=10;
			jw=1;
		}
		ans+=num+'0';
	}
	if(jw) ans+=jw+'0';
	reverse(ans.begin(),ans.end());
	return ans;
}
int main()
{
	string s,t;
	cin>>s;
	t=s;
	int cnt=0;
	while(check(t)==0&&cnt<10){
		reverse(s.begin(),s.end());
		cout<<t<<" + "<<s<<" = ";
		t=add(s,t);
		cnt++;
		cout<<t<<endl;
		s=t;
	}
	if(cnt<10) cout<<t<<" is a palindromic number.\n";
	else cout<<"Not found in 10 iterations.\n";
}

1137 Final Grading (25 分)

简单的结构体排序题,类似1141

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct stu{
	string id;
	int gp=-1;
	int gm=-1;
	int gf=-1;
	double G;
}s[maxn];
map<string,int>mp;
bool cmp(stu a,stu b){
	if(a.G!=b.G) return a.G>b.G;
	return a.id<b.id;
}
int main(){
	int p,m,n;
	cin>>p>>m>>n;
	int cnt=0;
	for(int i=0;i<p;i++){
		string id;
		int g;
		cin>>id>>g;
		if(mp.count(id)==0){
			mp[id]=cnt++;
		}
		int j=mp[id];
		s[j].id=id;
		s[j].gp=g;
	}
	for(int i=0;i<m;i++){
		string id;
		int g;
		cin>>id>>g;
		if(mp.count(id)==0){
			mp[id]=cnt++;
		}
		int j=mp[id];
		s[j].gm=g;
	}
	for(int i=0;i<n;i++){
		string id;
		int g;
		cin>>id>>g;
		if(mp.count(id)==0){
			mp[id]=cnt++;
		}
		int j=mp[id];
		s[j].gf=g;
	}
	for(int i=0;i<cnt;i++){
		if(s[i].gm>s[i].gf){
			s[i].G=int(s[i].gm*0.4+s[i].gf*0.6+0.5);
		}
		else s[i].G=s[i].gf;
	}
	sort(s,s+cnt,cmp);
	for(int i=0;i<cnt;i++){
		if(s[i].gp>=200&&s[i].gp<=900&&s[i].G>=60&&s[i].G<=100){
			cout<<s[i].id<<" "<<s[i].gp<<" "<<s[i].gm<<" "<<s[i].gf<<" "<<s[i].G<<endl;
		}
	}
	return 0;
}

1138 Postorder Traversal (25 分)

题目大意:
已知二叉树的先序和中序遍历,输出其后序遍历的第一个节点。

可套模板:根据先序遍历和中序遍历求后序遍历

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int in[maxn],pre[maxn];
vector<int>ans;
int n;
void post(int root,int l,int r){
	if(l>r||l>=n) return;
	int k=pre[root];
	int j=l;
	while(j<r&&in[j]!=k) j++;
	post(root+1,l,j-1);
	post(root+j-l+1,j+1,r);
	ans.pb(k);
}
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>pre[i];
	}
	for(int i=0;i<n;i++){
		cin>>in[i];
	}
	post(0,0,n);
	cout<<ans[0];
	cout<<endl;
	return 0;
}

1139 First Contact (30 分)

题目大意:
A对B一见钟情,A找到它的朋友C,然后C再去找它的朋友D,(D同时是B的朋友),最后通过D与B建立联系。

由于个人的保守无知,一开始题还没完全看完就急于敲题了,直到我连样例都没过。。。
于是重新读题,发现原来A和B还可以是同性,于是有两种情况:
1、A和B是异性,那么A要去找一个他的同性朋友C,而C要去找一个与B同性(与自己异性)的B和C的共同好友D。
2、A和B是同性,那么A要去找一个他的同性朋友C,而且C不能就是B,(也就是说他们俩必须通过其他人建立联系,而不能直接联系)然后C再去找一个同性朋友D(D 既不能是A,也不能是B),最后再通过D去找B。
理清楚之后,就是简单模拟啦。用邻接表来存储朋友信息。
还需要注意:如果遇到这种数据:-0000,用整数来处理就会丢失性别信息,无法判断是男是女了。所以直接用string输入,先判断性别,再转为int

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
vector<int>e[maxn];
int vis[maxn];
bool cmp(const pair<int,int>&a,const pair<int,int>&b){
	if(a.fi!=b.fi) return a.fi<b.fi;
	else return a.se<b.se;
}
int main(){
	int n,m;
	cin>>n>>m;
	memset(vis,-1,sizeof(vis));//男生标记为-1
	while(m--){
		string u,v;
		cin>>u>>v;
		int a=stoi(u);
		int b=stoi(v);
		if(u[0]=='-'){
		   	a=-a;
			vis[a]=1;//女生标记为1
		}
		if(v[0]=='-'){
			b=-b;
			vis[b]=1;			
		}
		e[a].pb(b);
		e[b].pb(a);
	}
	int q;
	cin>>q;
	while(q--){
		int x,y;
		cin>>x>>y;
		if(x<0) x=-x;
		if(y<0) y=-y;
		int flag;
		if(vis[x]==vis[y]) flag=0;//同性
		else flag=1;//异性
		int cnt=0;
		vector<pair<int,int> >ans;
		for(int i=0;i<e[x].size();i++){
			int a=e[x][i];
			if(vis[x]==vis[a]&&a!=y){
				for(int j=0;j<e[a].size();j++){
					int b=e[a][j];
					if(flag){
						if(vis[a]+vis[b]==0&&b!=y&&b!=x){
							for(int k=0;k<e[b].size();k++){
								if(e[b][k]==y&&e[b][k]!=x){
									ans.pb(mpi(a,b));
									cnt++;
								}
							}
						}
					}
					else{
						if(vis[a]==vis[b]&&b!=y&&b!=x){
							for(int k=0;k<e[b].size();k++){
								if(e[b][k]==y&&e[b][k]!=x){
									ans.pb(mpi(a,b));
									cnt++;
                                    break;
								}
							}							
						}
					}
					
				}
			}
		}
		cout<<cnt<<endl;
		sort(ans.begin(),ans.end(),cmp);
		for(int i=0;i<ans.size();i++){
			printf("%04d %04d\n",ans[i].fi,ans[i].se);
		}
		
	}
	return 0;
}

1140 Look-and-say Sequence (20 分)

简单模拟,数字怎么读就怎么写

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
string read(string x){
	string ans="";
	for(int i=0;i<x.size();i++){
		char ch=x[i];
		int cnt=0;
		int j=i;
		while(x[j]==ch){
			j++;
			cnt++;//统计字符出现次数
		}
		ans+=(ch+to_string(cnt));
		i=j-1;
	}
	return ans;
}
int main(){
	int n,k;
	cin>>n>>k; 
	k--;
	string x=to_string(n);
	while(k--){
		x=read(x);
	}	
	cout<<x<<endl;
	return 0;
}

1141 PAT Ranking of Institutions (25 分)

结构体排序
需要注意的点是:计算每个学校总得分时,
ScoreB/1.5 + ScoreA + ScoreT*1.5
不能在计算过程中直接取整,而是要在全部算完之后,才取整。
这题由于自己的粗心,找bug找了很久很久,看了很多题解,都说是因为取整的问题,可我从头到尾取整都没有问题。最后万万没想到,时因为cmp函数里面,少写了一个return!!!
就很无语。。。
所以一定要注重细节呀!否则浪费时间,且影响心情。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
unordered_map<string,int>mp;
struct stu{
	string name;
	double sum=0.0;
    int ans;
	int ns;
}sa[maxn];
bool cmp(stu a,stu b){
	if(a.ans!=b.ans) return a.ans>b.ans;
	if(a.ns!=b.ns) return a.ns<b.ns;
	else return a.name<b.name;
}
int main(){
	int n;
	cin>>n;
	int cnt=0;
	for(int i=0;i<n;i++){
		string id,name;
		double g;
		cin>>id>>g>>name;
		for(int j=0;j<name.size();j++){
			if(name[j]>='A'&&name[j]<='Z'){
				name[j]+=32;
			}
		}
        if(id[0]=='B') g/=1.5;
		else if(id[0]=='T') g*=1.5;
		if(mp.count(name)==0){
			mp[name]=cnt++;
		}
        int j=mp[name];
        sa[j].name=name;
        sa[j].sum+=g;
        sa[j].ns++;
	}
	for(int i=0;i<cnt;i++){
		sa[i].ans=(int)sa[i].sum;//取整
	}
	cout<<cnt<<endl;
	sort(sa,sa+cnt,cmp);
	int rank=1;
	for(int i=0;i<cnt;i++){
		if(i>=1&&sa[i].ans!=sa[i-1].ans){
			rank=i+1;
		}
		cout<<rank<<" "<<sa[i].name<<" "<<sa[i].ans<<" "<<sa[i].ns<<endl;
	}
	return 0;
}

1142 Maximal Clique (25 分)

题目大意:
在无向图中,clique这个点集中的任意两个不同点之间都是相连的。若往clique中再加入一个新的结点就不再是clique了,则说明该点集是maximal clique。
分别给出顶点和无向边数Nv和Ne,以及每条边所连两个顶点的序号(1-Nv),判断所给顶点集是否构成maximal clique或者clique。

看完题目,我想到了有向图的强连通分量,感觉差不多。
用邻接矩阵存图,依据clique的定义,暴力遍历图。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int e[maxn][maxn];
int vis[maxn];
int a[maxn];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) e[i][j]=0;
			else e[i][j]=INF;
		}
	}
	while(m--){
		int u,v;
		cin>>u>>v;
		e[u][v]=e[v][u]=1;
	}
	int q;
	cin>>q;
	while(q--){
		int k;
		cin>>k;
		memset(vis,0,sizeof(vis));
		for(int i=0;i<k;i++){
			cin>>a[i];
			vis[a[i]]=1;
		}
		int flag=1;
		for(int i=0;flag&&i<k;i++){
			for(int j=0;j<k;j++){
				if(e[a[i]][a[j]]==INF){//点集中存在不相连的两个点,说明不是clique
					flag=0;
					break;
				}
			}
		}
		if(!flag) puts("Not a Clique");
		else{
			int cnt;
			for(int i=1;i<=n;i++){
				if(!vis[i]){
					cnt=0;
					for(int j=0;j<k;j++){
						if(e[i][a[j]]!=INF){
							cnt++;
						}
					}
					if(cnt==k){//如果不在该点集中的点,也与点集中所有点相连,则说明不是maximal clique
						flag=0;
						break;
					}
				}
			}
			if(flag) puts("Yes");
			else puts("Not Maximal");
		}
	}
	return 0;
}

1143 Lowest Common Ancestor (30 分)

题目大意:
给出二叉搜索树的先序遍历,求任意节点的最近公共祖先。

二叉搜索树有一个性质:将所有节点从小到大排序,即可得到其中序遍历。
所以题目相当于:已知二叉树的先序和中序遍历,求最近公共祖先。这就很简单啦,跟1151题一样的做法。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int pre[maxn],in[maxn];
int vis[maxn],pos[maxn];
int n,m;
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>pre[i];
		in[i]=pre[i];
		vis[pre[i]]=1;
	}
	sort(in+1,in+n+1);
	for(int i=1;i<=n;i++){
		pos[in[i]]=i;
	}
	while(m--){
		int a,b;
		cin>>a>>b;
		if(!vis[a]&&vis[b]) printf("ERROR: %d is not found.\n",a);
		else if(vis[a]&&!vis[b]) printf("ERROR: %d is not found.\n",b);
		else if(!vis[a]&&!vis[b]) printf("ERROR: %d and %d are not found.\n",a,b);
		else{
			int l=pos[a],r=pos[b];
			if(l>r) swap(l,r);
			int lca;
			for(int i=1;i<=n;i++){
				int k=pre[i];
				if(pos[k]>=l&&pos[k]<=r){
					lca=k;
					break;
				}
			}
			if(lca==a) printf("%d is an ancestor of %d.\n",a,b);
			else if(lca==b) printf("%d is an ancestor of %d.\n",b,a);
			else printf("LCA of %d and %d is %d.\n",a,b,lca);
		}
	}
	return 0;
}

1144 The Missing Number (20 分)

暴力,注意数据范围

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int INF=0x3f3f3f3f;
map<int,int>vis;
int main()
{
	int n;
	cin>>n;
	while(n--){
		int x;
		cin>>x;
		if(x>0) vis[x]=1;
	}
	for(int i=1;;i++){
		if(!vis[i]){
			cout<<i<<endl;
			break;
		}
	}
}

1145 Hashing - Average Search Time (25 分)

题目大意:
给出一个序列,对其进行哈希,且哈希函数为 H a s h ( k e y ) = k e y % s i z e Hash(key)=key\%size Hash(key)=key%size,产生冲突时,用二次探测的方法解决(增量序列只考虑正数部分)。
给出另一个序列,依次在上述所建的哈希表中进行元素查找,并计算平均查找时间。

之前没怎么用过哈希,也就数据结构课上学过,忘得差不多了。。。所以样例推了很久很久,才搞懂
而且构建哈希表所需“时间”,和在哈希表中查找元素所需时间是不太一样的!
这里所说的查找时间,其实就是指查找次数
详细记录一下,样例是怎么得到的:

Sample Input:
4 5 4
10 6 4 15 11
11 4 15 2
Sample Output:
15 cannot be inserted.
2.8

创建哈希表
1、哈希表长度不是质数,修改为第一个大于4的质数:5
2、将元素逐个插入哈希表
10%5=0,6%5=1,4%5=4 未产生冲突,直接插入
在这里插入图片描述
3、接下来插入15:15%5=0,产生冲突,进行二次探测:
H i = ( H ( k e y ) + d i ) H_i=(H(key)+d_i)%size Hi=(H(key)+di),其中 d i = 1 2 , 2 2 , . . . , i 2 d_i=1^2,2^2,...,i^2 di=1222,...,i2, 1 ≤ i < s i z e ) 1\leq i < size) 1i<size)

为什么i<size?
当i=size时, ( H ( k e y ) + s i z e 2 ) % s i z e = = H ( k e y ) (H(key)+size^2)\%size==H(key) (H(key)+size2)%size==H(key),无需再重复探测一遍

在这里插入图片描述
11的插入同上,最终的哈希表结果如下:
在这里插入图片描述
查找元素:
H i = ( H ( k e y ) + d i ) H_i=(H(key)+d_i)%size Hi=(H(key)+di),其中 d i = 1 2 , 2 2 , . . . , i 2 d_i=1^2,2^2,...,i^2 di=1222,...,i2, ( 1 ≤ i ≤ s i z e ) (1\leq i \leq size) (1isize)
(ps:我也还没搞清楚,为什么这里i要一直到等于size,但是如果不等于的话,次数就会少算一次。。。)

1、查找11:11%5=1,而hash[1]=6,故进行二次检测。hash[1+1]=11,查找成功,查找时间(次数)为:2
2、查找4:4%5=4,而hash[4]=4,查找成功,查找时间:1
3、查找15:15%5=0–>(0+1)%5=1–>4%5=4–>9%5=4–>16%5=1–>25%5=0,查找时间:6
4、查找2:2%5=2,而hash[2]=6,继续hash[2+1]=0。找到这里可以确定不可能找到2了,因为如果哈希表里有2的话,就应该可以插入到这个位置,可现在这个位置却是空的,所有只能说明:里面没有2这个元素。查找时间:2
5、计算平均查找时间:(2+1+6+2)/4=2.8

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
bool prim(int x){
	if(x<2) return false;
	for(int i=2;i*i<=x;i++){
		if(x%i==0) return false;
	}
	return true;
}
int ha[maxn];
int s,n,m;
void insert(int x){
	int tmp=x%s;
	if(ha[tmp]==0){
		ha[tmp]=x;
	}
	else{
		int flag=0;
		for(int j=1;j<s;j++){
			int y=(tmp+j*j)%s;
			if(ha[y]==0){
				ha[y]=x;
				flag=1;
				break;
			}
		}
		if(!flag) cout<<x<<" cannot be inserted.\n";
	}
}
int find(int x){
	int tmp=x%s;
	int num=0;
    for(int j=0;j<=s;j++){
        int y=(tmp+j*j)%s;
        num++;
        if(ha[y]==x||ha[y]==0){
            break;
        }
    }
    cout<<x<<" "<<num<<endl;
	return num;
}
int main(){
	cin>>s>>n>>m;
	while(prim(s)==false){
		s++;
	}
	for(int i=0;i<n;i++){
		int x;
		cin>>x;
		insert(x);
	}
	double ans=0;
	for(int i=0;i<m;i++){
		int x;
		cin>>x;
		ans+=find(x);
	}
	printf("%.1lf\n",ans/m);
	return 0;
}

1146 Topological Order (25 分)

题目大意:
给出一个有向无环图,判断某序列是否为该有向无环图的拓扑排序。

简单模拟:遍历一遍拓扑序列,每次更新有向图中顶点的入度,当前拓扑序列元素入度大于0,就不是拓扑排序。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);} 
vector<int>e[maxn];
int in[maxn];
int in1[maxn];
vector<int>ans;
int main(){
	int n,m;
	cin>>n>>m;
	while(m--){
		int u,v;
		cin>>u>>v;
		e[u].pb(v);
		in[v]++;
	}
	int k;
	cin>>k;
	for(int i=0;i<k;i++){
		int flag=0;
		for(int j=1;j<=n;j++){
			in1[j]=in[j];
			//cout<<j<<" "<<in[j]<<endl;
		}
		for(int i=0;i<n;i++){
			int x;
			cin>>x;
			//cout<<in1[x]<<endl;
			if(in1[x]>0) flag=1;
			else{
				for(auto j:e[x]){
					in1[j]--;
				}
			}
		}
		if(flag){
			ans.pb(i);
		}
	}
	cout<<ans[0];
	for(int i=1;i<ans.size();i++){
		cout<<" "<<ans[i];
	}
    cout<<endl;
	return 0;
}

1147 Heaps (30 分)

题目大意:
给出完全二叉树的层序遍历,判断该完全二叉树是大顶堆还是小顶堆,或者根本不是堆。最后根据层序遍历求出后序遍历。

1、根据堆的性质进行判断:若每一颗子树均满足父节点的值大于等于它所有子节点的值,则是大顶堆;若每一颗子树均满足父节点的值大小于等于它所有子节点的值,则是小顶堆;否则不是堆。
2、知道层序,相当于知道了父子关系,很容易便能求出后序遍历。注意一下输出格式就好。
同时注意n>=2
所以最开始不要直接写a[1]>=a[2]&&a[1]>=a[3]来判断是大顶堆还是小顶堆。。。
a[3]可能根本没有,然后就出现段错误。。。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);} 
int a[maxn];
vector<int>ans;
int m,n;
void print(int x){
	if(x>n){
		return;
	}
	int lc=2*x;
	int rc=2*x+1;
	if(lc<=n) print(lc);
	if(rc<=n) print(rc);
	ans.pb(a[x]);
}
int main(){
	cin>>m>>n;
	while(m--){
		ans.clear();
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		int mmax=0;
		int mmin=0;
		if(a[1]>=a[2]) mmax=1;
		else mmin=1;
		for(int i=1;i<=n;i++){
			int lc=2*i;
			int rc=2*i+1;
			if(mmax&&((lc<=n&&a[lc]>a[i])||(rc<=n&&a[rc]>a[i]))){
				mmax=0;
				break;
			}
			else if(mmin&&((lc<=n&&a[lc]<a[i])||(rc<=n&&a[rc]<a[i]))){
				mmin=0;
				break;
			}
		}
		if(mmax) puts("Max Heap");
		else if(mmin) puts("Min Heap");
		else puts("Not Heap");
		print(1);
		cout<<ans[0];
		for(int i=1;i<ans.size();i++){
			cout<<" "<<ans[i];
		}
		cout<<endl;
	}
	return 0;
}

1148 Werewolf - Simple Version (20 分)

题目大意:
狼人杀:n名玩家,两个狼人,两个人说谎(其中必有一个狼人,一个平民),根据玩家得陈述,判断谁是狼人。

一开始想着枚举说谎的人,可是就算确定了说谎的两个人,还得继续确定到底哪两位是狼人。过于麻烦了。
直奔目标:
枚举狼人,同时可以确定撒谎的人(把好人说成狼人,狼人说成好人的人),最后判断是否只有两个人说谎且说谎的人中只有一个狼人,即可。而且这样枚举的话,最后的结果一定是字典序最小的。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e4+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
vector<int>lie;
int a[maxn];
int b[maxn];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=1;//最开始把所有人标记成好人
	}
	int flag=0;
	for(int i=1;i<n;i++){
		for(int j=i+1;j<=n;j++){
			b[i]=-1;//枚举狼人
			b[j]=-1;
			lie.clear();//记录撒谎的人
			for(int k=1;k<=n;k++){
				if(a[k]*b[abs(a[k])]<0) lie.pb(k);//陈述与事实相反,则为撒谎
			}
			if(lie.size()==2&&b[lie[0]]*b[lie[1]]<0){//只有两个人撒谎且其中一人为狼人
				cout<<i<<" "<<j<<endl;
				return 0;
			}
			b[j]=1;
		}
		b[i]=1;
	}
	puts("No Solution");
	return 0;
}

1149 Dangerous Goods Packaging (25 分)

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
vector<int>v[maxn];
int vis[maxn];
int g[maxn];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		int x,y;
		cin>>x>>y;
		v[x].pb(y);//把不能同时出现的数字记录下来
		v[y].pb(x);
	}
	while(m--){
		memset(vis,0,sizeof(vis));
		int k;
		cin>>k;
		for(int i=0;i<k;i++){
			cin>>g[i];
			vis[g[i]]=1;
		}
		int flag=0;
		for(int i=0;i<k;i++){
			for(auto j:v[g[i]]){
				if(vis[j]){//一旦不能同时出现的,都有,就break
					flag=1;
					break;
				}
			}
			if(flag) break;
		}
		if(!flag) puts("Yes");
		else puts("No");
	}
	return 0;
}

1150 Travelling Salesman Problem (25 分)

题目大意:
旅行商问题:从某城市出发,能否不重复的走完所有城市。
按照题目意思来就好。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e3+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int e[maxn][maxn];//存边
int vis[maxn];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) e[i][j]=1;
			else e[i][j]=INF;
		}
	}
	for(int i=0;i<m;i++){
		int a,b,d;
		cin>>a>>b>>d;
		e[a][b]=e[b][a]=d;
	}
	int p;
	cin>>p;
	int mmin=INF;
	int id;
	for(int t=1;t<=p;t++){
		int k;
		cin>>k;
		memset(vis,0,sizeof(vis)); 
		int s;
		cin>>s;//起点城市
		vis[s]++;//访问次数
		int x=s;
		int sum=0;
		int flag=1;
		for(int i=1;i<k;i++){
			int y;
			cin>>y;
			vis[y]++;
			if(e[x][y]==INF){//无法到达,输出NA
				flag=0;
			}
			else sum+=e[x][y];//计算路径总和
			x=y;//更新出发点
		}
		printf("Path %d: ",t);
		if(!flag){
			printf("NA (Not a TS cycle)\n");
		}
		else{
			int cnt=0;
			int flag1=0;
			for(int i=1;i<=n;i++){
				if(vis[i]==0){
					flag1=1;
					break;
				}
				if(i!=s&&vis[i]>=2) cnt++;
			}
			if(vis[s]==1||flag1){//没有回到起点或者存在未被访问过的城市
				printf("%d (Not a TS cycle)\n",sum);
			}
			else{
				if(mmin>sum){//求最短路
					mmin=sum;
					id=t;
				}
				if(cnt==0){
					printf("%d (TS simple cycle)\n",sum);
				}
				else printf("%d (TS cycle)\n",sum);//除起点外,还存在被访问多次的城市
			}
		}
		 
	}
	printf("Shortest Dist(%d) = %d\n",id,mmin);
	
	return 0;
}

1151 LCA in a Binary Tree (30 分)

题目大意:
给出二叉树的中序遍历和先序遍历序列,求任意两节点的最近公共祖先(LCA)

之前做过一道顺序存储的二叉树求LCA,觉得特别方便,通过先序和中序得到每个节点的序号,可是就超时了。。。确实,因为我反复用map,每一次都要遍历一遍map,求下标对应的节点。

后来发现,原来根本不用想那么复杂,建树什么的也没有必要。
在中序遍历中,最近公共祖先的位置一定在U和V之间。(包括U和V)
然后再去先序遍历里面找第一个满足“在中序遍历中的位置出现在U和V之间(包括U和V)”的节点,这个节点就是最近公共祖先了。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
map<int,int>mp;
int in[maxn],pre[maxn];
int main(){
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>in[i];
		mp[in[i]]=i;//记录节点在中序遍历中的位置
	}
	for(int i=1;i<=n;i++){
		cin>>pre[i];
	}
	for(int i=0;i<m;i++){
		int a,b;
		cin>>a>>b;
		int l=mp[a];
		int r=mp[b];
		if(l&&!r) printf("ERROR: %d is not found.\n",b);
		else if(!l&&r) printf("ERROR: %d is not found.\n",a);
		else if(!l&&!r) printf("ERROR: %d and %d are not found.\n",a,b);
		else if(l==r) printf("%d is an ancestor of %d.\n",a,b);
		else{
			if(l>r) swap(l,r);//注意交换l,r
			for(int j=1;j<=n;j++){
				if(mp[pre[j]]>=l&&mp[pre[j]]<=r){
					if(pre[j]==a) printf("%d is an ancestor of %d.\n",a,b);
					else if(pre[j]==b) printf("%d is an ancestor of %d.\n",b,a);
					else printf("LCA of %d and %d is %d.\n",a,b,pre[j]);
					break;
				}
			}
		}
	}
	return 0;
}

1152 Google Recruitment (20 分)

给定一长度为n的字符串,寻找其是否存在长度为k且为质数的子串。

暴力即可。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
bool check(string s){
	ll num=0;
	//stoi(s);直接这样也可
	for(int i=0;i<s.size();i++){//转化为整数
		num=num*10+s[i]-'0';
	}
	//判断质数
	if(num<2) return false;
	for(ll i=2;i*i<=num;i++){
		if(num%i==0) return false;
	}
	return true;
}
int main(){
	int n,k;
	cin>>n>>k;
	string s;
	cin>>s;
	for(int i=0;i<=n-k;i++){//注意循环截止条件i<=n-k
		string t=s.substr(i,k);
		if(check(t)){
			cout<<t<<endl;
			return 0;
		}
	}
	puts("404");
	return 0;
}

1153 Decode Registration Card of PAT (25 分)

用结构体记录信息,将card number拆成四个部分:等级、考场号、考试时间、座位号
对于第一种询问:同样用结构体将同一等级的人的card number和考试成绩记录下来,然后排序输出
对于第二种查询:直接遍历结构体,将对应考场的学生成绩相加即可
对于第三种查询:用unordered_map统计相同考场的人数(map会超时),然后按value值排序。
注意:查询输入时什么样,输出就什么样,不要忽略前缀零
如:输入查询 2 00107
则输出 2 00107
而不是 2 107。。。
用printf输出string:printf(“%s\n”,s.c_str());

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e4+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
struct stu{
	string id;
	char type;
	int site,date,num,grade;
}s[maxn],t[maxn];
bool cmp(stu a,stu b){
	if(a.grade!=b.grade) return a.grade>b.grade;
	else return a.id<b.id;
}
unordered_map<int,int>mp;
vector<pair<int,int> >v;
bool cmp1(const pair<int,int> &p1,const pair<int,int> &p2)
{
	if(p1.se!=p2.se) return p1.se>p2.se;
	else return p1.fi<p2.fi;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		string t;
		int g;
		cin>>t>>g;
		s[i].id=t;
		s[i].type=t[0];
		s[i].site=stoi(t.substr(1,3));
		s[i].date=stoi(t.substr(4,6));
		s[i].num=stoi(t.substr(10,3));
        s[i].grade=g;
	}
	for(int i=0;i<m;i++){
		int ty;
		cin>>ty;
		printf("Case %d: %d ",i+1,ty);
		if(ty==1){
			char ch;
			cin>>ch;
			printf("%c\n",ch);
			int k=0;
			for(int j=0;j<n;j++){
				if(s[j].type==ch){
					t[k].id=s[j].id;
					t[k++].grade=s[j].grade;
				}
			}
			if(k==0) puts("NA");
			else{
				sort(t,t+k,cmp);
				for(int j=0;j<k;j++){
					cout<<t[j].id<<" ";
					printf("%d\n",t[j].grade);
				}
			}
		}
		if(ty==2){
			string ad;
			cin>>ad;
		    cout<<ad<<endl;
		    int add=stoi(ad);
			int sum=0;
			int ans=0;
			for(int j=0;j<n;j++){
				if(s[j].site==add){
					ans++;
					sum+=s[j].grade;
				}
			}
			if(ans==0) puts("NA");
			else printf("%d %d\n",ans,sum);
		}
		if(ty==3){
			string ti;
			cin>>ti;
			cout<<ti<<endl;
			int time=stoi(ti);
			int flag=0;
			mp.clear();
			for(int j=0;j<n;j++){
				if(s[j].date==time){
					flag=1;
					mp[s[j].site]++;
				}
			}
			if(flag==0) puts("NA");
			else{
				v.clear();
				unordered_map<int,int>::iterator it;
				for(it=mp.begin();it!=mp.end();++it){
					v.pb(mpi(it->first,it->second));
				}
				sort(v.begin(),v.end(),cmp1);
				vector<pair<int,int> >::iterator it2;
			    for (it2=v.begin();it2!=v.end();++it2){
			    	if(it2->second==0) break;
			        printf("%03d %d\n",it2->first,it2->second);
			    }
			}
		}
	}
	return 0;
}

1154 Vertex Coloring (25 分)

题目大意:
同一条边的两个顶点颜色不能相同。
判断某涂色方案是否符合上述条件,若符合,统计不同颜色的个数,否则输出no

简单粗暴:先存图(事实证明,不管用结构体存还是vector邻接表存都可以,不会超时),然后统计颜色个数(set)
最后遍历所有边,如果出现同一边的两个顶点同色的情况,直接结束。
一直在想会不会超时,可能是acm赛制做太多了,搞的心理恐惧,pat怕啥?交了再说>_<

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int col[maxn];
vector<int>e[maxn];
set<int>s;
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	while(m--){
		int u,v;
		scanf("%d%d",&u,&v);
		e[u].pb(v);
		e[v].pb(u);
	} 
	int k;
	scanf("%d",&k);
	while(k--){
		int num=0;
		s.clear();
		for(int i=0;i<n;i++){
			scanf("%d",&col[i]);
			s.insert(col[i]);
		}
		int flag=0;
		for(int i=0;i<n;i++){
			for(auto j:e[i]){
				if(col[i]==col[j]){
					flag=1;
					break;
				}
			}
			if(flag) break;
		}
		if(flag) puts("No");
		else printf("%d-coloring\n",s.size());		
	}
	return 0;
}

1155 Heap Paths (30 分)

题目大意:
给出一棵完全二叉树,需打印出所有从根节点到叶子节点的路径(从右往左输出),并判断出该二叉树是小顶堆还是大顶堆或者不是堆。

从根节点到叶子结点的路径:先序遍历;从右往左输出:先序遍历的镜像。
记录路径:dfs一边搜索,一边记录,然后打印输出
vector存储路径,通过push和pop回溯,维护路径
判断是否是堆:
如果满足:父节点均大于/小于他的所有子节点,则是大顶堆/小顶堆;否则啥也不是。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
typedef long long ll;
inline ll lowbit(ll x){ return x&(-x);}
int a[maxn];
vector<int>v;
int vis[maxn];
int n;
void dfs(int x){
	int lc=2*x;
	int rc=2*x+1;
	if(lc>n&&rc>n){
		if(x<=n){//打印路径
			cout<<v[0];
			for(int i=1;i<v.size();i++){
				cout<<" "<<v[i];
			}
			cout<<endl;
		}
		return;
	}
	//先搜右子树,在搜左子树(相当于先序遍历的镜像)
	v.pb(a[rc]);
	dfs(rc);
	v.pop_back();//回溯!!!
	v.pb(a[lc]);
	dfs(lc);
	v.pop_back();
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	v.pb(a[1]);
	dfs(1);
	int flag=0;
	if(a[1]>a[2]) flag=1;//大顶堆
	else flag=2;//小顶堆
	for(int i=2;i<=n;i++){
		int lc=2*i;
		int rc=2*i+1;
		if(flag==1&&((lc<=n&&a[i]<a[lc])||(rc<=n&&a[i]<a[rc]))){
			flag=0;
			break;
		} 
		if(flag==2&&((lc<=n&&a[i]>a[lc])||(rc<=n&&a[i]>a[rc]))){
			flag=0;
			break;
		}
	}	
	if(flag==1){
		puts("Max Heap");
	}
	else if(flag==2){
		puts("Min Heap");
	}
	else puts("Not Heap");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值