天梯赛模拟选题1

L2-022 重排链表 - 团体程序设计天梯赛-练习集 (pintia.cn)

难度主要是在想怎么去建立这个链表,模拟比较容易

看代码熟悉下stl::list的操作

#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N = 1e6 + 10, mod = 1e9 + 7;

int start, n;

struct node
{
	int ads, x, next;
}a[N];

void solve()
{	
	list<node> mylist;
	
	cin >> start >> n;
	
	int tmp = start;
	
	for(int i = 1; i <= n; i ++ )
	{
		int ads, x, next;
		cin >> ads >> x >> next;
		a[ads] = {ads, x, next};
		/* 首先链表间肯定需要地址来链接,既然用数组的形式存链表,那肯定要用数组的下标地址来
        作为链接依据(跟数组要通过下标找数一个道理),又因为答案要输出排序后的链表的地址,
        数据和next,那每一个节点自然就要存ads,x,next了 */
	}
	
	while(start != -1) //从开始位置按顺序找好链表,终止条件为-1 
	{
		mylist.push_back(a[start]); //尾插 
		start = a[start].next;
	}
	
	vector<node> ans; 
	while(mylist.size()) //模拟,就是取一个后面的然后取一个前面的 
	{
		ans.push_back(mylist.back());  mylist.pop_back();
		if(!mylist.size()) break;
		ans.push_back(mylist.front());  mylist.pop_front();
	}
	
    //注意链表重排后,对应的next地址都要发生变化,用vector存答案更好实现输出
	for(int i = 0; i < ans.size() - 1; i ++ )
		printf("%05d %d %05d\n", ans[i].ads, ans[i].x, ans[i + 1].ads);
	printf("%05d %d %d\n", ans[ans.size() - 1].ads, ans[ans.size() - 1].x, -1);
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

L3-011 直捣黄龙 - 团体程序设计天梯赛-练习集 (pintia.cn)

题意:找到一条解放城镇最多(意思就是最短路途径的城市最多),且杀敌数最多的一条最短路,并要记录路径

附加信息dijkstra,多条最短路的更新选择问题,可以作板子,具体看代码

#include<bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> PII;

const int N = 510;

int n, m;
string c1, c2;
vector<PII> g[N]; //存图 

int s[N]; //每个节点敌军的数量 

int sum[N]; //路径中的杀敌数量和 

int pre[N]; //记录路径 

int dist[N]; //最短距离 

int cnt[N]; //最短路径的条数 

int city[N]; //途径的城市数 

bool st[N]; //dijk板子 

map<string, int> p; //存城市和下标的映射 
map<int, string> p2;

void dijkstra()
{
	memset(dist, 0x3f, sizeof dist);
	priority_queue<PII, vector<PII>, greater<PII>> q;
	q.push({0, p[c1]});
	dist[p[c1]] = 0;
	cnt[p[c1]] = 1;
	pre[p[c1]] = -1;
	
	for(int i = 0; i < n; i ++ ) sum[i] = s[i];
	for(int i = 0; i < n; i ++ ) city[i] = 1;
	
	while(q.size())
	{
		auto t = q.top();
		q.pop();
		
		int u = t.second;
		if(st[u]) continue;
		st[u] = 1;
		
		for(int i = 0; i < g[u].size(); i ++ )
		{
			int nx = g[u][i].first;
			int d = g[u][i].second;
			if(dist[nx] > dist[u] + d) 
			{
				dist[nx] = dist[u] + d;
				cnt[nx] = cnt[u]; //继承最短路径条数 
				sum[nx] = sum[u] + s[nx]; //路径上的杀敌数量累加 
				pre[nx] = u; //记录路径 
				city[nx] = city[u] + 1; //到该点的城市数等于上一点的城市数加一 
				q.push({dist[nx], nx});
			}
			//这里是关键,若是有多条最短路时根据题目条件的处理 
			else if(dist[nx] == dist[u] + d) 
			{
				cnt[nx] += cnt[u];
				if(city[nx] < city[u] + 1)
				{
					//若途径的城市数量不相等,则要途径最多的一条 
					city[nx] = city[u] + 1;
					sum[nx] = sum[u] + s[nx]; //注意所有消息都要更新 
					pre[nx] = u;
				}
				else if(city[nx] == city[u] + 1)
				{
					 //若途径城市数相等,再考虑要杀敌数最多的一条 
					if(sum[nx] < sum[u] + s[nx])
					{
						sum[nx] = sum[u] + s[nx];
						pre[nx] = u;
					}	
				} 
			}
		}
	}
}

void solve()
{
	cin >> n >> m >> c1 >> c2;
	
	p[c1] = 0;
	p2[0] = c1;
	for(int i = 1; i <= n - 1; i ++ )
	{
		int x;
		string tmp;
		cin >> tmp >> x;
		s[i] = x;
		p[tmp] = i;
		p2[i] = tmp;
	}
	
	for(int i = 1; i <= m; i ++ )
	{
		string u, v;
		int w;
		cin >> u >> v >> w;
		int x = p[u], y = p[v]; 
		g[x].push_back({y, w});
		g[y].push_back({x, w});
	}
	
	dijkstra();
	
	int ed = p[c2];
	stack<int> st;
	int tot = 0;
	
	while(ed != -1) //从后往前找路径 
	{
		st.push(ed);
		ed = pre[ed];
	}
	
	while(st.size())
	{
		cout << p2[st.top()];
		st.pop();
		if(st.size() != 0) cout << "->";
	}
	
	cout << endl;
	cout << cnt[p[c2]] << " " << dist[p[c2]] << " " << sum[p[c2]] << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

L2-013 红色警报 - 团体程序设计天梯赛-练习集 (pintia.cn)

题意:若删除一个城市(即删掉这个城市和其他城市的连边)后,图的连通性发生改变,则发出红色警报,否则不发出警报

思路:1.用并查集来维护连通性,每一轮动态的删掉一个城市,然后通过下一轮(再删掉前i个城市后)重新连边,等价于删掉该城市的所有连边

2.每一轮统计一下连通子图的数量,若连通子图数量不变或者只是增加了1(就是这个城市被分出去了不影响其他的城市的连通性),则不需发出红色警报,否则需要

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N = 510;

int n, m;
int p[N];
bool del[N];
//del数组用来记录被删掉的城市 

struct node
{
	int a, b;
}e[5100];

int find(int x)
{
	if(p[x] != x) p[x] = find(p[x]);
	return p[x];
}

void merge(int a, int b)
{
	int pa = find(a);
	int pb = find(b);
	p[pa] = pb;
}

int count() //查询当前有多少个连通子图 
{
	int tot = 0;
	for(int i = 0; i < n; i ++ )
		if(find(i) == i) tot ++; //若find(i) == i,说明是一个连通子图 
	return tot;	
}

void solve()
{
	cin >> n >> m;
	
	for(int i = 0; i < n; i ++ ) p[i] = i;
	
	for(int i = 1; i <= m; i ++ )
	{
		int x, y;
		cin >> x >> y;
		e[i].a = x, e[i].b = y;
		merge(x, y);
	}
	
	int k;
	cin >> k;
	int ori = count();
	for(int i = 1; i <= k; i ++ )
	{
		int x;
		cin >> x; 
		del[x] = 1; //表明已经删掉了 
		for(int j = 0; j < n; j ++ ) p[j] = j; //重新初始化并查集 
		
		for(int j = 1; j <= m; j ++ )
		{
			if(del[e[j].a] || del[e[j].b]) continue;
			merge(e[j].a, e[j].b);
		}	
		
		int af = count();
		if(ori == af || ori + 1 == af)
			printf("City %d is lost.\n", x);
		else
			printf("Red Alert: City %d is lost!\n", x);

		ori = count(); //更新当前的连通子图的数量 
	}
	
	if(k == n) cout << "Game Over." << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

L3-010 是否完全二叉搜索树 - 团体程序设计天梯赛-练习集 (pintia.cn)

主要是学习用数组建树的思路,直接看代码

#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N = 100;

struct NOI
{
	int x;
	int l, r;
	int deep;
}tr[N]; //节点,可任意添加维护各种信息
//基本信息就是数据,左右节点 

vector<int> ans;
map<int, int> p;
int rt;
int cnt;

void build(int u, int x) //二叉搜索树建树, 用cnt来建立链接 
{
	if(x > tr[u].x)
	{
		if(tr[u].l == 0) //找到第一个左儿子为空的位置插入 
		{
			cnt ++;
			tr[u].l = cnt;
			tr[cnt].x = x;
			tr[cnt].deep = tr[u].deep + 1;
			p[cnt] = x;
		}
		else build(tr[u].l, x);
	}
	else
	{
		if(tr[u].r == 0)
		{
			cnt ++;
			tr[u].r = cnt;
			tr[cnt].x = x;
			tr[cnt].deep = tr[u].deep + 1;
			p[cnt] = x; 
		}
		else build(tr[u].r, x);
	}
}

void bfs() //层序遍历
{
	queue<int> q;
	q.push(rt);
	
	while(q.size())
	{
		int u = q.front();
		q.pop();

        ans.push_back(u);
		if(tr[u].l != 0) q.push(tr[u].l);
		if(tr[u].r != 0) q.push(tr[u].r);
	}
}

void solve()
{
	int n;
	cin >> n;
	
	int x;
	cin >> x;
	
	//初始化1为根节点去建树 
	cnt ++;
	rt = cnt;
	tr[cnt].x = x;
	tr[cnt].deep = 1;
	p[cnt] = x;
	
	for(int i = 2; i <= n; i ++ )
	{
		int x;
		cin >> x;
		build(rt, x);
	}
	
	set<int> st;
	int d1 = 0, d2 = 0;
	for(int i = 1; i <= cnt; i ++ )
		st.insert(tr[i].deep);
	
	for(auto c : st)
	{
		if(c >= d1) d2 = d1, d1 = c;
		else if(c > d2) d2 = c;
	}
	
	int sum = 0;
	for(int i = 1; i <= cnt; i ++ )
		if(tr[i].deep == d2)
			sum ++;
	
	bfs();	

    for(int i = 0; i < ans.size(); i ++ )
    {
        cout << p[ans[i]];
        if(i != ans.size() - 1) cout << " ";
    }
    cout << endl;
    
	if(sum == (int)pow(2, d2 - 1)) cout << "YES" << endl;
	else cout << "NO" << endl;
}

signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

L2-014 列车调度 - 团体程序设计天梯赛-练习集 (pintia.cn)

题意:思维题,就是问在这个序列中顺次入列的话最少能拆出几个单调下降序列

思路:1. q数组来存每个序列的尾部(一开始一个序列也没有),每次若在现有序列的尾部中能找到一个比它大的数(去找第一个比它大的数,贪心的思想,显然接在第一个比它大的数后,浪费的资源最少),那么这个数就可以插入这个队列中,并更新队列尾部值,若找不到,那只能另开一个新的数列,造成贡献

2. 可以发现q数组是有序的(单调增加),故可以考虑二分,原因:每次新增一个序列时,新加入的数一定比当前所有队列的尾部数字都大;再者就算前面的队列尾部数字有更新,那也一定是变得更小,所以q数组是单调递增的

#include<bits/stdc++.h>
#define int long long 
 
using namespace std;
 
const int N = 1e5 + 10;
 
int a[N];
int q[N];
int cnt;
 
void solve()
{
	int n;
	cin >> n;
	
	for(int i = 1; i <= n; i ++ ) cin >> a[i];
	
	for(int i = 1; i <= n; i ++ )
	{
		int l = 1, r = cnt;
		int ans = -1;
		while(l <= r)
		{
			int mid = (l + r) / 2;
			if(q[mid] > a[i]) 
			{
				r = mid - 1;
				ans = mid;
			}
			else l = mid + 1;
		}
		
		//若能找到一个比它大的数,插入当前序列的尾部 
		if(ans != -1) q[ans] = a[i];  
		else q[++ cnt] = a[i];
		//否则新开一个序列 
	}
	
	cout << cnt << endl;
}
 
signed main()
{
	int t = 1;
	while(t -- ) solve();
	return 0;
}

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值