2025 湖南大学程序设计竞赛(补题)
诶呀,真的把时间给忘记掉了,本来是下午准备打的,但是前一天给记成了晚上,导致晚上的周赛也错过了。真的是有点麻瓜了哈。。。。
题目意思:
给定数组,判断在交换两个数或者不交换的情况下数组是否是升序。
思路:
贪心,直接找到不符合题目两个数字,进行交换,最后判断交换的数组是否是升序。非常麻瓜的一种思想,写法也非常的无脑。
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve() {int m;cin>>m;vector<int>ac(m+1);for(int i=1;i<=m;i++){cin>>ac[i];}int sum=0;bool k=0;int qi=-1;//找到第一个不满足的数字int hou=-1;//第二个不满足的数字for(int i=2;i<=m;i++){if(ac[i]<ac[i-1]){sum++;if(k==0){k=1;qi=i-1;}else{hou=i;}}}if(sum==0){//如果数组本身就是升序的情况cout<<"Sorted"<<endl;return;}else if(sum>2){//有多个数字要交换的情况cout<<"Failed"<<endl;return;}else{swap(ac[qi],ac[hou]);//对原先的两个数字进行交换,最后判断for(int i=2;i<=m;i++){if(ac[i]<ac[i-1]){cout<<"Failed"<<endl;return;}}cout<<"Sorted"<<endl;return;}
}
signed main() {int n=1;//cin>>n;while(n--)solve();return 0;
}
题目意思:
给定一个初始整数 m=n,每次操作将 m 随机替换为 0 到 m−1 之间的一个均匀随机整数,直到 m=0。求期望的操作次数。
思路:
设 E[m] 表示从 m 开始到 0 的期望操作次数
E[0]=0,初始化
但是,m在题目中是10的5次方,较大,预处理E[m]的时间复杂度会比较大 ,显然我们第一眼想到的是前缀和进行优化。
因此,只剩下了两种写法。
一个是对E[m]的不断更新,一个是对S[m]的不断更新
#include <bits/stdc++.h>
using namespace std;
const int nima = 1e5;
vector<double> E(nima + 1, 0.0);
vector<double> S(nima + 1, 0.0);
int main() {E[0] = 0.0;S[0] = 0.0;for (int m = 1; m <= nima; ++m) {E[m] = 1.0 + S[m - 1] / m;S[m] = S[m - 1] + E[m];}int T;cin >> T;while (T--) {int n;cin >> n;cout << fixed << setprecision(7) << E[n] <<endl;}
}
题目意思:
给定n个房间,每个房间有若干个可以传送的门,每一个门可以传送到任意位置,包括自身。
判定是否无论怎样对门进行配对,总有一个出口,即从1一定能到n。
思路:
给定n个节点,第i个节点需要和自身或其他节点连边ai次。 问1号点是否一定能到达n号点。
我们把每一个门想成一条边,每个点之间看成节点。故,可以画出一个简单的图。
题目给出所有的ai之和一定是偶数,我们从连通块的角度切入看,由于一条边连接两个点,所以
一个连通块的ai之和一定是一个偶数。
若数组 a 中只有 a1 和 an 为奇数,考虑 a1 所在连通块,只要不向 an 连边,就不可能满足奇偶性,故答案为 Yes。
反之,令每个节点优先向自身连边。若 a1 为偶数,答案为 No;若 a1 为奇数,必然可以找到另一个非 an 的奇数与之连接,答案同样为 No。
故:
特判只有全数组a1和an为奇数的情况下才满足条件
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline void solve(){int m;cin>>m;vector<int>a(m+1);bool ok=0;for(int i=1;i<=m;i++){cin>>a[i];if(i>=2&&i!=m){if(a[i]&1)ok=1;//判断除了第一个和最后一一个数字之外的奇数情况}}if(m==1){//数组里面只有一个数字,特判cout<<"Yes"<<endl;return;}if(a[1]&1&&a[m]&1&&!ok){cout<<"Yes"<<endl;return;}cout<<"No"<<endl;return;
}
signed main(){int n;cin>>n;while(n--){solve();}
}
题目意思:
给出四种情况,分别算出各个情况下的线段长度。
思路:
第一种情况我们用高中的三角函数思想运用圆心角解决。
圆心角对应的弦长是d*sin(a/2)
第二三种情况刚好是相反的。
最后一种直接输出圆的直径。
#include <bits/stdc++.h>
using namespace std;
#define int long long
double PI=3.14159265;
inline void solve(){string ac;cin>>ac;double C=2*PI*50;//先对周长初始化cout<<fixed<< setprecision(7);//保留7位小数if(ac[1]=='-'){int answer=abs((ac[0]-'0')-(ac[2]-'0'));if(answer>4){answer=8-answer;}//这里sin的函数内的值对应的是弧度制下的我们还要进行转换cout<<100*sin((45*answer*PI)/(360))<<endl;}else if(ac[1]=='>'){if(ac[0]<ac[2]){cout<<C*(ac[2]-ac[0])/8<<endl;//注意这里要是看是劣弧还是优弧}else{cout<<C*(8+ac[2]-ac[0])/8<<endl;}}else if(ac[1]=='<'){int sum=abs(ac[0]-ac[2]);if(ac[0]>ac[2]){cout<<C*(sum)/8<<endl;}else{cout<<C*(8-sum)/8<<endl;}}else{cout<<100<<endl;}
}
signed main(){int n;cin>>n;while(n--){solve();}
}
题目意思:
定义数组前缀和Sn,如果前缀和里面所有的数字都是正整数,定义为good数组,对数组进行n次循环左移的操作,输出n次操作中good数组的个数。
思路:
暴力想法TLE,我们不妨用滑动窗口的思想,记录每一次移动后最小的前缀和数字。
最后通过判断最小前缀和数字和原先前缀和的大小关系来判断是否是good数组。
其实最后的判断大小大家可以自己推一遍,会很清晰。
判断大小就是变相的减去。
#include <bits/stdc++.h>
using namespace std;
const int op=8e18;
int main() {int n;cin >> n;vector<int> a(n);for (int i = 0; i < n; i++) {cin >> a[i];}// 计算扩展为2n长度的前缀和数组vector<long long> pre(2 * n + 1, 0);for (int i = 0; i < 2 * n; i++) {pre[i + 1] = pre[i] + a[i % n];}// 使用单调队列来维护窗口中的最小值deque<int> q;vector<long long> suf(2 * n + 1, op);// 初始化前n个元素的窗口for (int i = 1; i <= n; i++) {while (!q.empty() && pre[i] <= pre[q.back()]) {q.pop_back();}q.push_back(i);}suf[0] = pre[q.front()];//suf存储的是第i次向左移动后的最小前缀和// 滑动窗口处理后续元素for (int i = n + 1; i <= 2 * n; i++) {// 移除窗口外的元素while (!q.empty() && q.front() < i - n) {q.pop_front();}// 维护队列单调性while (!q.empty() && pre[i] <= pre[q.back()]) {q.pop_back();}q.push_back(i);// 当前窗口的最小值suf[i - n] = pre[q.front()];}// 计算结果int goodCount = 0;for (int s = 0; s < n; s++) {if (suf[s] >= pre[s]) {//通过比较第i次移动的最小前缀和与前i项之和的比较可以判断是否满足题目条件goodCount++;}}cout << goodCount << endl;return 0;
}