【牛客训练记录】第六届山东师范大学与齐鲁工业大学大学生程序设计联赛
训练情况

赛后反思
F题一血因为 QLU 技术原因被吃了,题目看太急了没看到有空格寄了一发,尽力局,除了动态规划DP那道C题,其他感觉还挺满意的,剩下可能就真不会了
C题
考虑动态规划,DP[i][0/1] 表示第 \(i\) 位涂成红/蓝色的答案,同色加上对应颜色答案贡献和额外的答案贡献,异色加上对应的颜色答案贡献,动态规划的转移方程如下
\[dp[i][0] = max(dp[i-1][0] + a[i] + c[i-1] + c[i],dp[i-1][1] + a[i])
\]
\[dp[i][1] = max(dp[i-1][1] + b[i] + c[i-1] + c[i],dp[i-1][0] + b[i])
\]
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve(){
int n; cin>>n;
vector<int> a(n + 1),b(n + 1),c(n + 1);
for(int i = 1;i<=n;i++) cin>>a[i];
for(int i = 1;i<=n;i++) cin>>b[i];
for(int i = 1;i<=n;i++) cin>>c[i];
vector<vector<int>> dp(n + 1,vector<int>(2));
dp[1][0] = a[1];
dp[1][1] = b[1];
for(int i = 2;i<=n;i++){
dp[i][0] = max(dp[i-1][0] + a[i] + c[i-1] + c[i],dp[i-1][1] + a[i]);
dp[i][1] = max(dp[i-1][1] + b[i] + c[i-1] + c[i],dp[i-1][0] + b[i]);
}
cout<<max(dp[n][0],dp[n][1])<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
D题
求环的最短和最长长度,我们可以使用并查集(DSU)维护环的关系,最后计数每一个环的长度求 min 和 max 即可。
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
const int N = 5007;
int n;
int a[N];
int fa[N];
int cnt[N];
int Find(int x){
if(fa[x] == x) return x;
return fa[x] = Find(fa[x]);
}
void Union(int x,int y){
x = Find(x); y = Find(y);
if(x == y) return;
fa[y] = x;
}
void solve(){
cin>>n;
for(int i = 1;i<=n;i++) cin>>a[i],fa[i] = i;
for(int i = 1;i<=n;i++){
Union(i,a[i]);
}
for(int i = 1;i<=n;i++){
cnt[Find(i)]++;
}
int ma = -N,mi = N;
for(int i = 1;i<=N-7;i++){
if(cnt[i] == 0) continue;
ma = max(ma,cnt[i]);
mi = min(mi,cnt[i]);
}
cout<<mi<<" "<<ma<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
F题
我的一血被吃了,这题有空格需要使用 getline(因为这个问题 WA 了一发),判断字符串中是否有 English。
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
void solve(){
string s; getline(cin,s);
int ans = 0;
for(int i = 0;i<s.size()-6;i++){
string ss = s.substr(i,7);
if(ss == "English") ans++;
}
cout<<ans<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
I题
纯排序题,有点像 ICPC 2024 第二场网络赛的那道题,我们计算交换两个位置前后对答案的贡献,我们发现按照 \(\frac{t}{h}\) 从小到大排序才能让答案最小,一样的情况下取编号小的。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 3;
int n;
struct node{
int id,t,h;
}a[N];
bool cmp(node x,node y){
if(x.h*y.t == y.h*x.t) return x.id < y.id;
else return x.t*y.h < y.t*x.h;
}
void solve(){
cin>>n;
for(int i = 1;i<=n;i++){
cin>>a[i].t>>a[i].h;
a[i].id = i;
}
sort(a+1,a+1+n,cmp);
for(int i = 1;i<=n;i++){
cout<<a[i].id<<" ";
}
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
J题
图论 Dijkstra 最短路模板,只需要在入优先队列之前判断一下这个节点是否崩坏,还没崩坏就入队即可。
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
const int N = 1e5 + 3;
int n,m;
int t[N];
int head[N],tot;
struct node{
int id,dis;
bool operator <(const node &x)const{
return dis > x.dis;
}
};
struct edge{
int u,v,w,nxt;
}edge[N<<1];
int dist[N];
bool vis[N];
priority_queue<node> q;
void dij(){
memset(dist,0x3f3f3f3f,sizeof(dist));
dist[1] = 0;
q.push((node){1,0});
while(q.size()){
int x = q.top().id; q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x];i;i = edge[i].nxt){
int v = edge[i].v;
int w = edge[i].w;
if(dist[v] > dist[x] + w && dist[x] + w < t[v]){
dist[v] = dist[x] + w;
q.push((node){v,dist[v]});
}
}
}
}
void add_edge(int u,int v,int w){
edge[++tot].u = u;
edge[tot].v = v;
edge[tot].w = w;
edge[tot].nxt = head[u];
head[u] = tot;
}
void solve(){
cin>>n>>m;
for(int i = 1;i<=n;i++) cin>>t[i];
for(int i = 1;i<=m;i++){
int u,v,w; cin>>u>>v>>w;
add_edge(u,v,w);
add_edge(v,u,w);
}
dij();
if(dist[n] == 0x3f3f3f3f) cout<<"NO"<<endl;
else cout<<"YES"<<endl<<dist[n]<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
L题
由题目可知一个人能喝 \(\frac{1}{3}\) 桶水,所以我们只要判断 \(x \times y\) 是否是 \(3\) 的倍数,如果是输出 \(\frac{xy}{3}\),否则输出 xiaoshu。
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
void solve(){
int x,y; cin>>x>>y;
if((x*y)%3==0) cout<<x*y/3<<endl;
else cout<<"xiaoshu"<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
M题
这题我们观察到数据范围很小,可以暴力枚举每一位放不放置障碍物,最后再BFS判断是否能走到终点即可。
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
int n;
string s[5];
string a[5];
int cho[70];
int ans = INT_MAX;
int u[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
int bfs(){
queue<pair<int,int>> q;
bool vis[70][70];
int stx,sty,edx,edy;
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
vis[i][j] = false;
if(a[i][j] == 's') stx = i,sty = j;
if(a[i][j] == 't') edx = i,edy = j;
}
}
for(int i = 0;i<n*n;i++){
if(cho[i]){
int nowx = i/n;
int nowy = i%n;
if(nowx == stx && nowy == sty) continue;
if(nowx == edx && nowy == edy) continue;
a[nowx][nowy] = 'x';
}
}
bool flag = false;
q.push(make_pair(stx,sty));
while(q.size()){
int x = q.front().first;
int y = q.front().second;
if(x == edx && y == edy) flag = true;
q.pop();
for(int i = 0;i<4;i++){
int xx = x + u[i][0];
int yy = y + u[i][1];
if(xx < 0 || xx >= n || yy < 0 || yy >= n || a[xx][yy] == 'x' || vis[xx][yy]) continue;
vis[xx][yy] = 1;
q.push(make_pair(xx,yy));
}
}
int nowans = 0;
for(int i = 0;i<n*n;i++) if(cho[i]) nowans++;
if(!flag) return nowans;
else return INT_MAX;
}
void pd(){
for(int i = 0;i<n;i++) a[i] = s[i];
int nowans = bfs();
if(nowans < ans) ans = nowans;
}
void dfs(int x){
if(x == n*n){
pd();
return;
}
for(int i = 0;i<=1;i++){
cho[x] = i;
dfs(x + 1);
cho[x] = 0;
}
}
void solve(){
cin>>n;
for(int i = 0;i<n;i++) cin>>s[i];
dfs(0);
cout<<ans<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}

浙公网安备 33010602011771号