提前預告,市賽初中組會考算法題,應該會有兩道模板題
比如DFS BFS 二分 簡單動態規劃,雖然我們沒學多久,但是模板題你還是要會寫的
A題 編輯距離 動態規劃
注意多組輸入
#include<iostream>
using namespace std;
int dp[1005][1005];
//dp[i][j]把s字符串的前i個經過一系列操作變成b字符串的前j個的最小代價
char s[1005];
char b[1005];
int main(){int n,m;while(scanf("%d%s%d%s",&n,s+1,&m,b+1)!=EOF){for(int i=0;i<=m;i++){dp[0][i]=i; //插入 }for(int i=0;i<=n;i++){dp[i][0]=i; //刪除 }for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i]==b[j])dp[i][j]=dp[i-1][j-1];//此時i j位置相同,可以直接從s[i-1]->b[j-1] 轉移過來else{dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1));/*dp[i-1][j]+1 表示我們把s[1~i] 刪掉i位置,得到s[1~i-1] 從它變到b[1~j] dp[i][j-1]+1 表示我們把s[1~i] 從它變到b[1~j-1] 然后插入一個b[j] dp[i-1][j-1]+1 從s[1~i-1] 從它變到b[1~j-1] 對于s[i] 直接修改為b[j] */} }}printf("%d\n",dp[n][m]);}return 0;
}
B題 最長上升子序列 (N^2)版本
#include<iostream>
using namespace std;
int A[1005];
int dp[1005]; //dp[i]表示以A[i]結尾的最長上升子序列元素
int main(){int n;scanf("%d",&n);int ans=1;for(int i=1;i<=n;i++){dp[i]=1;scanf("%d",&A[i]);for(int j=i-1;j>=1;j--){if(A[j]<A[i]){dp[i]=max(dp[i],dp[j]+1);}}//考慮拼接的方法,想尋得dp[i],往前面找,跟某個元素拼接起來 形成以A[i]//結尾的上升子序列,那么所有的子序列取max也就是最大的 ans=max(ans,dp[i]);//但是答案不一定是以A[n]結尾 }printf("%d",ans);return 0;
}
當然,其實還有優化寫法,利用二分,即可實現NlogN 的時間復雜度
我建議還是背一下(理解一下)
代碼不是完全的,請看看思路
ll dp[N];
ll a[N];
ll b[N];
signed main() {ll n;read(n);for(int i=1; i<=n; i++) {read(a[i]);}ll cnt=0;for(int i=1; i<=n; i++) {if(cnt==0||a[i]>dp[cnt]) {dp[++cnt]=a[i];//首位置要放入元素//如果當前元素A【i】比當前序列結尾的還要大,放進來 上升 continue;} else {//如果當前元素A[i]≤ 序列結尾 //考慮查找序列里面合適的值,替換掉 //舉例 1 100 2 //實際上用2替換100會更優,因為你過程的元素越大,越不利于后續上升dp[upper_bound(dp+1,dp+1+cnt,a[i])-dp]=a[i];}}printf("%lld",cnt);
}
右邊的數字即全球通過人數
C題題解
我覺得這是不能錯的題。 1 ? 1 1*1 1?1的格子不用說了,啥地方都能放
主要看 2 ? 2 2*2 2?2的,一個板只能放最多兩個 2 ? 2 2*2 2?2的
所以你要先計算出放 b b b個 2 ? 2 2*2 2?2的要多少板 ,以及這些板還有多少個格子沒放的。
如果多余沒放的格子足夠放完 a a a個 1 ? 1 1*1 1?1的 ,那么答案就是 2 ? 2 2*2 2?2需要的板子數
否則你還需要用(a-多余格子) 這么多個格子去計算還需要多少塊板
#include<bits/stdc++.h>
using namespace std;
int main(){int t;scanf("%d",&t);while(t--){int a,b; scanf("%d%d",&a,&b); int le=0;if(b%2==0)le=(15-8)*(b/2);if(b%2){le=(15-8)*(b/2)+15-4;}int ans=b/2+b%2;if(a<=le)printf("%d\n",ans);else{printf("%d\n",ans+(a-le)/15+((a-le)%15!=0)); }}return 0;
}
D題題解
這其實就是個簡單的模擬題,你把輸入的字符串字母sort一遍,把密碼表處理出來
然后枚舉字符串開始翻譯就行了
#include<bits/stdc++.h>
using namespace std;
char s[200005];
char b[30];
bool vis[30];
char sw[30];
int main(){int t;scanf("%d",&t);while(t--){memset(vis,false,sizeof(vis));int n;scanf("%d",&n);scanf("%s",s+1);int len=0;for(int i=1;i<=n;i++){if(vis[s[i]-'a'])continue;else{vis[s[i]-'a']=true;b[++len]=s[i];}}sort(b+1,b+1+len);for(int i=1;i<=len/2+1;i++){sw[b[i]-'a']=b[len-i+1];sw[b[len-i+1]-'a']=b[i];}for(int i=1;i<=n;i++){s[i]=sw[s[i]-'a'];}printf("%s\n",s+1);}return 0;
}
E題題解
這個標記題需要一定數理知識
對于一個三元組 A [ i ? 2 ] , A [ i ? 1 ] , A [ i ] {A[i-2],A[i-1],A[i]} A[i?2],A[i?1],A[i] 我們得標記它們,你可以想象一下,什么樣的三元組能相互之間算答案?有兩個元素一樣對不對,我們直接把一樣的元素標記起來,記為一個二元組。
以此標記該三元組里面的二元組,按順序標記
每次計算答案的時候,查找一下當前三元組前面,有多少個跟自己的二元組一樣的三元組,該操作不保證過濾了重復元素
因此我們需要查詢該三元組前面有多少個跟自己一模一樣的三元組,因為一模一樣是不會產生答案的,所以要減去3倍
#include<bits/stdc++.h>
using namespace std;
int A[200005];
map<pair<int,int>,int >vis_1;
map<pair<int,int>,int >vis_2;
map<pair<int,int>,int >vis_3;
map<pair<pair<int,int>,int> ,int >pre;
int main(){int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);long long int ans=0;for(int i=1;i<=n;i++){scanf("%d",&A[i]);if(i>=3){// 當前三元組A[i-2] A[i-1] A[i] // 三種可能: A[i-2]A[i]相等 A[i-1]A[i]相等 A[i-2]A[i-1]相等 這三個二元組可以作為標記去查詢ans=ans+vis_1[make_pair(A[i-2],A[i-1])];//統計前面有多少個跟A[i-2] A[i-1]值一樣的二元組(先不考慮前面存在跟自己完全一樣的三元組,那么答案就是加這個二元組標記的個數,視作前面出現的該二元組的元素與當前A[i]都不一樣)//A[i-2] A[i-1] ? 前面的一些三元組結構//A[i-2] A[i-1] A[i] 當前三元組 ans=ans+vis_2[make_pair(A[i-2],A[i])];ans=ans+vis_3[make_pair(A[i-1],A[i])]; vis_1[make_pair(A[i-2],A[i-1])]++;vis_2[make_pair(A[i-2],A[i])]++;vis_3[make_pair(A[i-1],A[i])]++;ans=ans-3*pre[make_pair(make_pair(A[i-2],A[i-1]),A[i])];//考慮存在重復的問題,舉例//如果前面有x個三元組滿足值與當前三元組(A[i-2],A[i-1],A[i])一樣,那么我們就多計算了x個答案,因為完全相等的三元組不產生答案貢獻,枚舉了三個二元組,所以減法要減去*3 pre[make_pair(make_pair(A[i-2],A[i-1]),A[i])]++;}}printf("%lld\n",ans);vis_1.clear();vis_2.clear();vis_3.clear();pre.clear();}return 0;
}
F題題解
考慮東西南北指令,劃分為兩部分
一個部分是: 北南湊一對,相當于抵消移動 東西湊一對,相當于抵消移動
第一部分完成后,未湊對的剩下來的只能是北/南里面的一種,剩下的我們要考慮能不能均分給兩個人,同理東西
計算北南的對數,東西的對數
北南可以按A人先的順序輪流分配
東西可以按B人先的順序輪流分配
接下來分配剩余的未配對的,注意如果剩余奇數個,肯定不能保證最終兩個人走在同一個地方
#include<bits/stdc++.h>
using namespace std;
char s[200005];
int vis[30];
int A[30];
int B[30];
int main(){int t;scanf("%d",&t);int N,S,E,W;N='N'-'A';S='S'-'A';E='E'-'A';W='W'-'A';while(t--){int n;scanf("%d",&n);scanf("%s",s+1);vis[N]=vis[S]=vis[E]=vis[W]=0;A[N]=A[S]=A[E]=A[W]=0;B[N]=B[S]=B[E]=B[W]=0;for(int i=1;i<=n;i++){vis[s[i]-'A']++;}int ns=min(vis[N],vis[S]);int ew=min(vis[E],vis[W]);//配對相消 int lens=max(vis[N],vis[S])-min(vis[N],vis[S]);int leew=max(vis[E],vis[W])-min(vis[E],vis[W]); for(int i=1;i<=ns;i++){if(i%2){A[N]++;A[S]++;}else{B[N]++;B[S]++;}}for(int i=1;i<=ew;i++){if(i%2){B[E]++;B[W]++;}else{A[E]++;A[W]++;}}//雙消+偶數//單消 + 偶 if(lens%2||leew%2){printf("NO\n");}else{int op;if(vis[N]>vis[S])op=N;else op=S;A[op]+=lens/2;B[op]+=lens/2;if(vis[E]>vis[W])op=E;else op=W;A[op]+=leew/2;B[op]+=leew/2;if((A[N]+A[S]+A[E]+A[W])==0||(B[N]+B[S]+B[E]+B[W])==0){printf("NO\n");continue;} for(int i=1;i<=n;i++){if(A[s[i]-'A']){A[s[i]-'A']--;printf("R");}else{B[s[i]-'A']--;printf("H");}}printf("\n");}}return 0;
}