PTA团体程序设计天梯赛篇(三) ------ 训练赛五

正整数A+B

题目链接

解题思路

  1. 有一个点是,对于第二个字符串我们需要用getline()来读入,是因为第二个字符串中间可能有空格。
  2. 对于字符判断是否合法,首先我们可以判断负数,对于负数就是第一个符号是否是’-’,之后就是是否有小数点,这个可以用find()函数来找。
  3. 之后判断是否含字母,如果不含,在看字符的数值是否越界了。

代码:

#include<iostream>
#include<string>
using namespace std;

int num1 , num2;
bool flag1 = true  ,flag2 = true;

void solve(string a , bool & flag , int & num){
    if(a[0] == '-' || a.find('.') != string::npos )cout<<'?' ,flag = false; //负数或者有小数点
    else {
        for(int i = 0 ; i < (int)a.size() ; ++i)if(a[i] >= 'a' && a[i] <= 'z') { //有字母
            flag = false;
            break;
        }
        if(flag){ // 检测数值是否合法
            for(int i = 0 ; i < (int)a.size() ; ++i)
            {
                
              num = num * 10 + a[i] - '0';
                if(num > 1000){
                    break;
                }
            }
            if(num == 0 || num > 1000)cout<<'?',flag = false;
            if(flag)
             cout<<num;
        }else 
            cout<<'?' ;
    }
}

int main(){
    string a ,b;
    cin>>a;
    getchar();
    getline(cin,b);
    solve(a,flag1,num1);
    cout<<" + ";
    solve(b,flag2,num2);
    cout<<" = ";
    if(flag1 && flag2)cout<<(num1 + num2) <<endl;
    else cout<<"?\n";
    return 0;
}

出租

题目链接

解题思路
这题是然我们将数字去重排序后,输出原数字在去重之后位置的下标。
对于去重我们可以用一个bool数组来记录,第一次出现的放入新的数组。之后按降序排序。之后对于每一个数字取找其对应的小标即可。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 11;
int a[N] , b[N];
bool st[N];
char ch;
bool cmp(int a ,int b){
    return a > b;
}

int main(){
    int k = 0 ;
    for(int i = 0 ; i < N ; ++i){
        cin>>ch;
        a[i] = ch-'0';
        if(!st[a[i]]){
            b[k++] = a[i];
            st[a[i]] = 1;
        }
    }
    sort(b , b+k,cmp);
    cout<<"int[] arr = new int[]{";
    int f = 0;
    for(int i = 0 ; i < k; ++i)
    {
        if(f)cout<<',';
        cout<<b[i] , f++;
    }
    cout<<"};\n";
    cout<<"int[] index = new int[]{";
    f = 0;
    for(int i = 0 ; i < N ; ++i)
        for(int j = 0 ; j < k ; ++j)
            if(a[i] == b[j]){
                if(f)cout<<',';
                cout<<j,f++;
            }
    cout<<"};";
    return 0;
}

判断素数

题目链接

解题思路

这个就是一个试除法来判断素数的题目,只不过注意的是,如果用 i*i <= n因为n是int的极限大小,应次当到达临界的 i +1的时候会爆 int 导致超时,因此需要用long long

代码:

#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 1e6;
int n ;
bool st[N];
bool check(int n){
    if(n == 2)return true;
    if(n == 1)return false;
    for(int i = 2 ; (LL)i * i  <= n; ++i)
        if(n % i == 0)return false;
    return true;
}

int main(){
    int n;
    cin>>n;
    while(n--){
        int x;
        cin>>x;
        if(check(x))cout<<"Yes\n" ;
        else cout<<"No\n";
    }
    return 0;
    
}

一帮一

题目链接

解题思路
这题需要理解好题目意思,我们是依次从前边开始去找最后边的一个异性,因此我么直接将所有的人放在一个结构体数组中,之后去找,在找的时候我们需要用一个bool数组来记录是否找过了。

代码

#include<iostream>
#include<string>
using namespace std;

const int N = 110;
int cnt , n;
bool bk[N];
struct node{
    int sex;
    string s;
}st[N];
int main(){
    cin>>n;
    for(int i = 1 ; i <= n ; ++i)
        cin>>st[i].sex>>st[i].s;
    for(int i = 1 ; i <= n ; ++i){
        if(!bk[i]){
        int sex = st[i].sex;
        cout<<st[i].s<<" ";
        int j  = n ;
            for(j = n ; j  ; --j)
                if(st[j].sex != sex &&!bk[j])break; // 找到最后边且没有找过的异性
         bk[i] = bk[j] = 1;
        cout<<st[j].s<<endl;
        }
    }
    
    return 0;
}

列车调度(二分 + 贪心)

题目链接

解题思路
这题可以说是一个很经典的贪心问题了,对于每一个轨道我们看成一个数组,如果后来的数字比其中一个最后的数字小,那么其就可以接在后边。因此我们对于每一个轨道我们只记录最后边的值,而如果有多个轨道符号,我们就选取值较小的这就是贪心,而我们会发现,这样建立的数字满足单调递增的性质,因此在找第一个比其大或者等于的位置我们可以二分去查找。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1e5 + 10 , INF = 0x3f3f3f3f;

int t[N], cnt;
int n ;
int main() {
	cin >> n;
	for (int i = 1; i <= n ; ++i) {
		int x;
		cin >> x;
		if (!cnt)
			t[cnt++] =  x;
		else {
			int j = 0;
            j = lower_bound(t,t+cnt,x) - t ;
			if (j >= cnt)
				t[cnt++] = x;
			else
				t[j] = x;
		}
	}
	cout << cnt << endl;
	return 0;
}

愿天下有情人都是失散多年的兄妹(搜索+公共祖先)

题目链接

解题思路
我们将关系进行建树(用邻接表来存)后,只要在搜索的时候,出现当后面搜索的那个人,有一个存在的关系节点,被访问过了就说明它们两个之间在五代之内存在关系。

注意事项
需要将父母的性别也记录下来,不然有几个点过不去。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int N = 1e5 + 10;

vector<int>g[N];
int n, m;
char sex[N];
bool st[N], key;

void dfs(int u, int h) {
    st[u] = 1;
	if (h >= 4)
		return ;
	for (int i = 0 ; i < g[u].size() ; ++i) {
		int x = g[u][i];
		if (!st[x]) 
			dfs(x, h + 1);
		 else
			key = 1;
	}
}

int main() {
	cin >> n ;
	for (int i = 1; i <= n ; ++i) {
		int a, b, c;
		char x;
		cin >> a >> x >> b >> c;
		sex[a] =  x;
		if (b != -1)
			g[a].push_back(b) ,sex[b] = 'M';
		if (c != -1)
			g[a].push_back(c) ,sex[c] = 'F';
	}
	cin >> m;
	while (m--) {
		int a, b;
		cin >> a >> b;
		if (sex[a] == sex[b])
			cout << "Never Mind\n";
		else {
			key = false;
			memset(st, 0, sizeof st);
			dfs(a, 0);
			dfs(b, 0);
			if (key)
				cout << "No\n";
			else
				cout << "Yes\n";
		}
	}
	return 0;
}

直捣黄龙(最短路 )

题目链接

题意:找到己方大本营到敌方大本营的一条最短路径,当这样的路径有多条时选择解放最多城镇的路径(就是经过的城市越多越好),当这样的路径也有多条时选择杀敌数最多的路。

解题思路:
Dijkstra (堆优化)+ 多重条件判断。

数据处理:用map容器对表示城市的字符串做数据映射,使得城市可以用0到N-1表示,方便建立邻接表,然后套上最短路径模板 + 三个条件判断。

  • path[i]:表示i的前一个节点是path[i]

代码:

#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <string>
#include <cstring>
using namespace std;
typedef pair<int, int>PII;
const int N = 1010;
int n, m, start, End ;
map<string, int>mp;
map<int, string>name;
int h[N], w[N], ne[N], e[N], cnt;
int d[N], path[N], kill[N], node[N], army[N], road[N], em[N];
bool st[N];
//d存最短路径,path存路径,kill存杀敌数,node存经过城市数量
//em存各城市的士兵,road存最短路数量
void add(int u, int v, int val) {
	e[++cnt] = v, ne[cnt] = h[u], w[cnt] = val, h[u] =  cnt;
}

void dijstra() {
	memset(path, -1, sizeof path);
	memset(d, 0x3f, sizeof d);
	d[start] = 0;
	priority_queue<PII>heap;
	heap.push({0, start});
	road[start] = 1; // 初始化
	while (heap.size()) {
		int u = heap.top().second;
		heap.pop();
		if (st[u])
			continue;
		st[u] = 1;
		for (int i = h[u] ; ~i ; i = ne[i]) {
			int v = e[i];
			if (!st[v] && d[v] > d[u] + w[i])
				heap.push({-(d[u] + w[i]), v});
			if (!st[v] && d[v] >= d[u] + w[i]) {
				if (d[v] > d[u] + w[i]) { // 如果通过u 节点到v 更近,那么需要走这条
					d[v] = d[u] + w[i];
					node[v] = node[u] + 1; //前面经过的城市数 + 这个城市
					kill[v] = kill[u] + em[v]; //前面所杀的敌人数 + 这个城市的士兵数
					path[v] = u, road[v] = road[u];
				} else { // 有别的路径到v 与通过u 距离一样
					road[v] += road[u];
					if (node[u] + 1 >= node[v]) { // 看经过的节点数量
						if (node[u] + 1 > node[v]) { // 如果通过u的数量更多
							kill[v] = kill[u] + em[v];
							node[v] = node[u] + 1;
							path[v] = u;
						} else {
							if (kill[u] + em[v] > kill[v]) { // 如果连经过的节点也一样看杀的敌人数量
								kill[v] = kill[u] + em[v];
								path[v] = u;
							}
						}
					}
				}
			}
		}
	}
}

void Find(int x) { //递归输出路径
	if (path[x] != -1) {
		Find(path[x]);
		cout << name[path[x]] << "->";
	}
}


int main() {
	memset(h, -1, sizeof h);
	cin >> n >> m;
	string s1, s, se, s2;
	cin >> s1 >> se;
	mp[s1] = 1, name[1] = s1;
	for (int i = 2 ; i <= n ; ++i) {
		int x, a;
		cin >> s >> x;
		mp[s] = i, em[i] = x;
		name[i] = s;
	}
	start = mp[s1], End = mp[se];
	while (m--) {
		int a, b, c ;
		cin >> s1 >> s2 >> c;
		a = mp[s1], b = mp[s2];
		add(a, b, c), add(b, a, c);
	}
	dijstra();
	Find(End);
	cout << se << endl;
	cout << road[End] << ' ' << d[End] << ' ' << kill[End] << endl;
	return 0;
}

是否完全二叉搜索树(完全二叉树)

题目链接

题目大意
题目是让我们建立一个搜索二叉树之后,按层次遍历输出,之后判断是否是完全二叉树。

解题思路
本题难点应该就是如何判断是否是完全二叉树了,我们通共完全二叉树的定义可以知道,当层次遍历到的第一个空节点的时候应该把所以节点都遍历过了,因此我们写一个类似于bfs的判断函数,如果队列为空或者当前节点是空就退出循环,如果退出循环后遍历的节点数没有到n就说明不是。

代码

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 30;
int n ;
typedef struct node {
	int val ;
	node *l, *r;
} TNode, *Tree;

void insert(Tree &t, int val) {
	if (t == NULL) {
		t = (Tree) malloc(sizeof (TNode));
		t->val = val;
		t->l = t->r = NULL;
		return ;
	} else if (val < t->val)
		insert(t->r, val);
	else
		insert(t->l, val);
}

bool judge(Tree t) {
	int cnt = 0;
	queue<Tree>q;
	q.push(t);
	while (q.size() ) {
		auto u = q.front();
		q.pop();
		if (u == NULL)
			break;
		cnt++;
		q.push(u->l);
		q.push(u->r);
	}
	return cnt == n;
}


void bfs(Tree t) {
	queue<Tree>q;
	q.push(t);
	int f = 0;
	while (q.size()) {
		auto u = q.front();
		q.pop();
		if (f)
			cout << " ";
		cout << u->val, f ++;
		if (u->l)
			q.push(u->l);
		if (u->r)
			q.push(u->r);
	}
}

int main() {
	Tree t = NULL ;
	cin >> n;
	for (int i = 1 ; i  <= n ; ++i) {
		int x;
		cin >> x;
		insert(t, x);
	}

	bfs(t);
	cout << endl;
	if (judge(t))
		cout << "YES\n";
	else
		cout << "NO\n";


	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值