本次是whr和zy出的题,题目质量也挺高,建议把能补的题都补了。
比赛链接
A题最短路问题:
这一题时间是两秒,而且数据范围也不是很大。我直接爆搜过的,也没卡掉,我先提供一个爆搜的代码:
思路:
从起点开始依次遍历每一个能入队的点(可以被更新的点)将其入队,取队头的点更新其他的点,直到队列为空,f[][]数组记录答案,f [ i ] [ j ] 表示 从起点到 ( i , j ) 的最短距离。
#include<bits/stdc++.h> #define int long long using namespace std; typedef long long ll; const int N=1000+3,M=N*2,mod=998244353; int n,m,x,y; int mp[N][N];//存地图 int f[N][N];//f[i][j]表示从起点到(i,j)的最短距离 int dx[]={0,1,0,-1};//---用dx和dy从0到3的值分别表示点右,下,左,上的坐标变化。 int dy[]={1,0,-1,0}; queue<pair<int,int>> que;//队列存点 void solve(){ cin>>n>>m>>x>>y; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>mp[i][j],f[i][j]=1e18;//初始化点到其他店的距离为正无穷 que.push({1,1});//起点入队 f[1][1]=0;//起点到自身的距离为零 while(!que.empty()){ auto t=que.front();que.pop(); int sx=t.first,sy=t.second;//去除队头的两个点 int dic=f[sx][sy]; for(int i=0;i<=3;++i){//依次遍历右下左上四个点 int tx=sx+dx[i],ty=sy+dy[i];//分别表示i从0到3时,起点的右下左上四个点 if(tx>=1&&tx<=n&&ty>=1&&ty<=m){ if(dic+mp[tx][ty]<f[tx][ty]){//如果这个点可以被更新就入队。 f[tx][ty]=dic+mp[tx][ty];//对该点进行更新 que.push({tx,ty});//该点入队 } } } } cout<<f[x][y]<<'\n';//输出答案 // for(int i=1;i<=n;++i){ // for(int j=1;j<=m;++j) // cout<<f[i][j]<<" "; // cout<<'\n'; // } } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _; _=1; //cin>>_; while(_--)solve(); } /* 10 10 3 6 10 10 1 10 10 10 10 10 10 10 1 10 1 10 10 10 10 10 10 10 1 10 1 10 1 1 10 10 10 10 1 10 1 10 1 10 10 10 10 10 1 10 1 1 1 10 10 10 10 10 1 10 1 10 10 10 10 10 10 10 1 10 1 1 1 10 10 10 10 10 1 10 1 10 1 10 10 10 10 10 1 10 1 10 1 10 10 10 10 10 1 1 1 10 1 1 1 1 1 1 */
这题也可以用堆优化版的dijkstar做,建议先去学一下堆优化版的dijkstar算法再来做这道题,我把一位同学的dijkstar算法做的粘在这里,可以借鉴一下:
#include <bits/stdc++.h> #define endl "\n" #define pii pair<int,pair<int,int>> #define debug(x) cout << #x << ": -----> " << x << endl; // typedef long long ll; // typedef unsigned long long ull; using namespace std; const int maxn=1100; int mp[maxn][maxn]; int dis[maxn][maxn]; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; int n,m,gx,gy; bool st[maxn][maxn]; int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m>>gx>>gy; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>mp[i][j]; } } priority_queue<pii,vector<pii>,greater<pii>> pq; pq.push({0,{1,1}}); memset(dis,0x3f,sizeof dis); dis[1][1]=0; while(pq.size()){ auto t=pq.top(); pq.pop(); int x=t.second.first,y=t.second.second; int dist=t.first; if(st[x][y]) continue; st[x][y]=true; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx<1||xx>n||yy<1||yy>m) continue; if(dis[xx][yy]>dist+mp[xx][yy]){ dis[xx][yy]=dist+mp[xx][yy]; pq.push({dis[xx][yy],{xx,yy}}); } if(xx==gx&&yy==gy){ cout<<dis[xx][yy]<<endl; return 0; } } } return 0; }
B题纯纯签到,没一点坑。略。。。。
C题我是用单调栈做的:
就是每入栈一个元素检查栈头的三个元素是否合法。合法既出栈。
#include<bits/stdc++.h> #define int long long using namespace std; typedef long long ll; const int N=2e5+3,M=N*2,mod=1e9+7; char stk[N];//栈 int n; string s; int cnt,ans;//cnt表示站内有几个元素,既指向栈头,ans记录答案 void solve(){ cin>>n>>s; int len=s.size(); for(int i=0;i<len;++i){ stk[++cnt]=s[i]; if(stk[cnt]=='c'&&cnt>=3&&stk[cnt-1]=='f'&&stk[cnt-2]=='z')cnt-=3,++ans; //栈头合法就出栈三个元素并且ans++; }cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _; _=1; while(_--)solve(); }
这道题有的同学是直接对原字符串进行处理,不过道理一样,我把这题一血代码粘一下,可以借鉴一下:
#include <bits/stdc++.h> using namespace std; int main() { int n; int a = 0; string s; cin >> n >> s; for (int i = 0; i < n; i++) { if (s[i] == 'z' && s[i + 1] == 'f' && s[i + 2] == 'c') { s.erase(i, 3); i = i - 3; a = a + 1; } } cout << a; return 0; }
D题我是用二分做的:
思路就是二分答案,看答案是否合法,输出最小值最大的合法答案:
我的做法思路比较简单,但实现对你们来说可能比较难。建议学好二分和前缀和思想后再来自己做一遍这一道题。
#include<bits/stdc++.h> #define int long long using namespace std; typedef long long ll; const int N=1e5+3,M=N*2,mod=1e9+7; int n,k,a[N],sa[N]; // sa[ ]记录前缀和 vector<int> ve; bool check(int mid){ int op=lower_bound(ve.begin(),ve.end(),mid)-ve.begin(); if(op*mid-sa[op]<k){ return false; }else return true; } void solve(){ cin>>n>>k; for(int i=1;i<=n;++i)cin>>a[i]; sort(a+1,a+1+n); for(int i=1;i<=n;++i)sa[i]=sa[i-1]+a[i],ve.push_back(a[i]); int l=0,r=1e8; while(l<r){ int mid=(l+r)>>1; if(check(mid))r=mid; else l=mid+1; } int op=lower_bound(ve.begin(),ve.end(),l)-ve.begin(); // cout<<"----"<<op<<'\n'; if(op*l-sa[op]>k)cout<<l-1<<'\n'; else cout<<l<<'\n'; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _; _=1; //cin>>_; while(_--)solve(); }```
第二种做法:也是二分加前缀和的做法,但实现不同
代码如下:
#include<bits/stdc++.h> using namespace std; #define int long long const int N=1e5+5; int a[N]; int n,k; bool cheak(int x){ int res=0; for(int i=1;i<=n;i++){ res+=(max(0ll,x-a[i])); } return res<=k; } signed main(){ //ios::sync_with_stdio(false);cin.tie(0); cin>>n>>k; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); int l=0,r=1e14; while(l<r){ int m=l+r+1>>1; if(cheak(m)) l=m; else r=m-1; } cout<<l; }
E题比较简单,大部分人都过了,我还是粘一下一个同学的代码吧。
#include <bits/stdc++.h> using namespace std; #define LL long long const int N=100; int n; vector<string> v[2]; int main() { cin >> n; for(int i=1; i<=n; i++) { int a; string name; cin >> a >> name; v[a].push_back(name); } for(int i=0,j=v[1].size()-1; i<v[0].size(); i++,j--){ cout << v[0][i] << " " << v[1][j] << endl; } return 0; }
F题:
这是去年ccpc河南省赛A题,其实这是一个简单dp题:
(去年这道题是队友签的,我比赛的时候都没看这道题,就赛后看了一眼)
代码及思路如下:
(建议学会01背包和完全背包,理解状态转移之后再来看这一道题)
//dp[i][0]表示前i个事件都没有选择使用技能 //dp[i][1]表示前i个事件已经选择使用技能了 int dp[N][2]; void solve() { memset(dp, 0, sizeof dp); int n; cin >> n; for (int i = 1; i <= n; i ++ ) { string op; int val; cin >> op >> val; if (op[0] == 'G')//对于get不管有没有使用技能, 金币数都要增加 { dp[i][0] = dp[i - 1][0] + val; dp[i][1] = dp[i - 1][1] + val; } else { //如果前i个事件都没有选择使用技能 dp[i][0] = max(dp[i - 1][0] - val, 0); //前i-1个事件没有选择使用技能,但是当前选择使用 //前i-1个事件已经使用过技能,当前不可用 //比较两种情况哪一种金币数最多 dp[i][1] = max(dp[i - 1][0], max(dp[i - 1][1] - val, 0)); } } cout << max(dp[n][0], dp[n][1]) << "\n"; }
G这道题有点麻烦,就是模拟,模拟,模拟,知道平年,闰年,知道一个月多少天,有耐心就能做出来。
代码如下:(这题我着实不想写,粘的一位同学的代码)
#include <bits/stdc++.h> using namespace std; #define LL long long const int N=100005; LL s,y,m,d,x,n; int days[20] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; void slove(){ cin >> s >> x >> n; y = s/10000; m = s/100%100; d = s%100; int t=n,f=0; while(n--){ d++; if(y%4==0 && m==2) f=1; else f=0; if(d>days[m]+f) { d = 1; if(++m>12) m=1,y++; } } cout << y << (m<10 ? "0":"") << m << (d<10 ? "0":"") << d << ' '; x--; cout << (x+t)%7+1 << endl; } int main() { int t; cin >> t; while(t--) slove(); return 0; }
H题过的人有点少,读懂题就可以做。。。不解释了,好好读题
#include<bits/stdc++.h> using namespace std; const int N=101; int a[N]; int ans[N][11][11]; signed main(){ int mx=0,n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; mx=max(a[i],mx); } int pre=0,x=0; for(int u=1;u<=mx;u++){ for(int k=1;k<=10;k++){ for(int i=1;i<=n;i++){ if(a[i]>=u){ if(pre!=i){ x++; ans[i][u][k]=x; } else{ x+=2; ans[i][u][k]=x; } pre=i; } } } } for(int i=1;i<=n;i++){ cout<<"ID"<<i<<"\n"; for(int j=1;j<=a[i];j++){ for(int k=1;k<=10;k++){ cout<<ans[i][j][k]<<" "; } cout<<"\n"; } } }
综上:
A题:爆搜/堆dijkstar算法
B题:签到
C题:栈
D题:二分+前缀和
E题:签到
F题:dp
G题:模拟,模拟,模拟
H题:模拟,模拟,模拟
A C D F这四道题有能力的一定要全部补了
这次题的质量很高,为出题人点赞。。。