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;
}