P7909 [CSP-J 2021] 分糖果 candy
-
涉及知識點:閱讀理解、分支+數學思維、循環
-
解析:本題能夠直接讀明白題目,但是要注意題目要求輸出的結果是自己單次能得到的最大值。
-
方法1:暴力枚舉,直接將 [L, R] 進行枚舉,和現有最大值進行比較,每次取最大值。
但是題目要求的數據范圍到 1e9,而競賽中一般認為計算機 1秒可執行的次數約在 5e8, 1e9必爆,故而該方法很遺憾只有70分,不能拿到滿分。 -
方法2:數學思想,如果每個小朋友至少可以有 2 次選取的機會(也就是 L/n < R/n 的時候),那么最大值就是 n-1;
否則當小朋友只有 1 次選擇的機會的時候,最大值為 R%n。
於是,O(1)的時間復雜度就解決了這個看似很難,但也確實不容易的題目。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n,l,r;
int slove1(){
int ans=0;
for(int k=l; k<=r; k++){
ans=max(ans, k%n);
}
return ans;
}
int slove2(){
int ans=0;
if(l/n < r/n) ans=n-1;
else ans=r%n;
return ans;
}
int main(){
freopen("candy.in", "r", stdin);
freopen("candy.out", "w", stdout);
while(cin>>n>>l>>r){
// cout<<slove1()<<endl;
cout<<slove2()<<endl;
}
fclose(stdin); fclose(stdout);
return 0;
}
P7910 [CSP-J 2021] 插入排序 sort
-
涉及知識點:插入排序、復雜度、算法優化
-
解析:題目較長,耐心讀完,發現題目好像不難,但是一看數據點,直接玩完,不過還是要冷靜下來動手去拿到最高的分數。
-
方法1:純模擬,什么都不思考,就按照題目模擬一遍,當然這樣只能拿到 36~52分。
-
方法2:將原位置排序后的現有位置記錄下來,但是還是用到了暴力排序,大概能拿到 70~80分。
-
方法3:方法2 的基礎上優化,題目中有一個重要信息:保證類型 1 的操作次數不超過 5000,也就是說我們的主要操作會發生在...查詢上,那么考慮如何減少查詢所需要的時間。肯定是開個數組直接記錄,保證 O(1) 查詢即可,之后的時間主要是在插入元素后的排序上,那么可以對此進行優化到 O(n),這樣保證整體復雜度為 O(n*q)。
注意:\(O(n*q)\) 不會帶入最大數據計算,因為其中有修改和查詢兩種操作,很難計算到精確值。 -
方法5:數學思維,分桶計數,不模擬交換,計數 a[x]左邊小於 a[x] 的元素個數 l,以及 a[x]右邊大於 a[x]的元素個數 r,發現 a[x]排序后的位置為:x+r-l, 但是這樣的方法同樣會超時,畢竟每個數都需要進行一次查找,大概能拿到 70~80分。
-
方法1 : 36分代碼
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T{
int id, value;
}a[N], b[N];
void insertSort(T arr[], int n){
for(int i=1; i<=n; i++)
for(int j=i; j>=2; j--)
if(arr[j].value < arr[j-1].value){
T t = arr[j-1]; arr[j-1] = arr[j]; arr[j] = t;
}
}
int find(T arr[], int n, T x){
for(int i=1; i<=n; i++)
if(arr[i].value==x.value && arr[i].id==x.id) return i;
return -1;
}
bool cmp(T a, T b){
return a.value<b.value;
}
int main(){
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
int n,q; scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++){
scanf("%d", &a[i].value); a[i].id=i;
}
for(int i=1; i<=q; i++){
int f,x,v; scanf("%d%d", &f, &x);
if(f==1){ //修改
scanf("%d", &v); a[x].value=v;
}else if(f==2){ //查詢
for(int i=1; i<=n; i++) b[i]=a[i];
insertSort(b, n);
printf("%d\n",find(b, n, a[x]));
}
}
fclose(stdin); fclose(stdout);
return 0;
}
- 方法1: 52分代碼,在模擬的基礎上,利用 sort 稍加改進,注意穩定排序
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T{
int id, value;
}a[N];
//因為不是完全模擬插入排序 ,所以cmp發生一點小改動,需要保證穩定排序
bool cmp(T a, T b) {
if(a.value!=b.value) return a.value<b.value;
return a.id<b.id;
}
int main(){
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
int n,q; scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++){
scanf("%d", &a[i].value); a[i].id=i;
}
sort(a+1, a+1+n, cmp);
for(int i=1; i<=q; i++){
int f,x,v; scanf("%d%d", &f, &x);
if(f==1){ // 修改
scanf("%d", &v);
for(int j=1; j<=n; j++){
if(a[j].id==x){
a[j].value=v; break;
}
}
sort(a+1, a+1+n, cmp);
}else if(f==2){ // 查詢
for(int j=1; j<=n; j++){
if(a[j].id==x){
printf("%d\n", j); break;
}
}
}
}
fclose(stdin); fclose(stdout);
return 0;
}
- 方法2 : 70~80分代碼,將原位置的現有位置記錄下來,不用每次都去查詢
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T {
int id,value;
}a[N];
int b[N];//存放原位置的現在下標
bool cmp(T a, T b) {
if(a.value!=b.value) return a.value<b.value;
return a.id<b.id;
}
int main() {
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
int n,q; scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++) {
scanf("%d", &a[i].value); a[i].id=i;
}
sort(a+1, a+1+n, cmp);
for(int i=1; i<=n; i++) b[a[i].id]=i;
int f,x,v;
for(int i=1; i<=q; i++) {
scanf("%d%d", &f, &x);
if(f==1) { //修改
scanf("%d", &v); a[b[x]].value=v;
sort(a+1, a+1+n, cmp);
for(int i=1; i<=n; i++) b[a[i].id]=i;
} else if(f==2) { //查詢
printf("%d\n", b[x]);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
- 方法3:100分代碼,由於數據只有一個無序,可以考慮對排序部分進行 O(n) 優化
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int n,q;
struct T {
int id,value;
bool operator< (const T&temp) const {
if(value!=temp.value) return value<temp.value;
return id<temp.id;
}
}a[N];
int b[N];//存放原位置的現在下標
void insertSort1(int x){ // 1次優化
T temp=a[b[x]];
for(int i=b[x]; i<n; i++) a[i]=a[i+1]; a[n]=temp;
for(int i=n; i>1; i--){
if(a[i]<a[i-1]) swap(a[i-1],a[i]);
else break;
}
}
void insertSort(int x){ // 2次優化
int p=b[x];
for(int i=b[x]-1; i>=1; i--){
if(a[p]<a[i]) swap(a[p],a[i]),p=i;
else break;
}
for(int i=b[x]+1; i<=n; i++){
if(a[i]<a[p]) swap(a[p],a[i]),p=i;
else break;
}
}
int main() {
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++) {
scanf("%d", &a[i].value); a[i].id=i;
}
sort(a+1, a+1+n);
for(int i=1; i<=n; i++) b[a[i].id]=i;
int f,x,v;
for(int i=1; i<=q; i++) {
scanf("%d%d", &f, &x);
if(f==1) { //修改
scanf("%d", &v); a[b[x]].value=v;
insertSort(x);
// sort(a+1, a+1+n);
for(int i=1; i<=n; i++) b[a[i].id]=i;
} else if(f==2) { //查詢
printf("%d\n", b[x]);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
- 方法4:70~80分代碼,帶有一點數學思想,具體推導過程如下圖。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int a[N];
int main(){
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
int n,q; cin>>n>>q;
for(int i=1; i<=n; i++) cin>>a[i];
for(int i=1; i<=q; i++){
int f,x,v; cin>>f>>x;
if(f==1){
cin>>v; a[x]=v;
}else{ //查詢
int l=0,r=0;
for(int i=1; i<x; i++) if(a[i]>a[x]) l++;
for(int i=x+1; i<=n; i++) if(a[i]<a[x]) r++;
cout<<x-l+r<<endl;
}
}
fclose(stdin); fclose(stdout);
return 0;
}
P7911 [CSP-J 2021] 網絡連接 network
-
涉及知識點:模擬
-
解析:一道大模擬,題目不難,但是很考驗代碼量,本題需要仔細讀題,
然后你會發現一個十分有趣的東西, 50% 的數據滿足性質 1,那么性質1 又是什么呢? -
就是說 ip合法,那么問題來了,本題最難的是什么?
不就是判斷 ip地址合法嘛,我假設 ip合法,直接開始做,也能得到 50分,不香嗎? -
50分代碼如下
點擊查看代碼
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
map<string, int> m;
//檢查 ip地址是否合法
bool check(string s){
return true;
}
int main(){
freopen("network.in", "r", stdin);
freopen("network.out", "w", stdout);
int n; cin>>n;
for(int i=1; i<=n; i++){
string op,ad;cin>>op>>ad;
if(!check(ad)){
cout<<"ERR"<<endl;
}else{
if(op=="Server"){
if(m[ad]==0){
m[ad]=i; cout<<"OK"<<endl;
}else{
cout<<"FAIL"<<endl;
}
}else if(op=="Client"){
if(m[ad]!=0){
cout<<m[ad]<<endl;
}else{
cout<<"FAIL"<<endl;
}
}
}
}
fclose(stdin); fclose(stdout);
return 0;
}
- 100分代碼
加上 ip地址合法的判斷,注意:這里我使用了sscanf/sprintf
的寫法,這里部分小伙伴沒用過,
那么也可以使用其他寫法,我只是覺得這樣更方便,如果沒學過這部分的同學可以參考如下鏈接 https://www.cnblogs.com/hellohebin/p/15456897.html
點擊查看代碼
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
map<string, int> m;
//檢查 ip地址是否合法
bool check(string s){
char buf[100], buf2[100];
for(int i=0; i<s.size(); i++) buf[i]=s[i]; buf[s.size()]='\0';
int a,b,c,d,e;
sscanf(buf, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &e);
sprintf(buf2, "%d.%d.%d.%d:%d", a, b, c, d, e);
// printf("%s\n", buf); //輸出調試
// printf("%s\n", buf2);
// printf("%d.%d.%d.%d:%d\n", a, b, c, d, e);
if(a<0||a>255||b<0||b>255||c<0||c>255||d<0||d>255||e<0||e>65535) return false;
if(strcmp(buf, buf2)) return false;
return true;
}
int main(){
freopen("network.in", "r", stdin);
freopen("network.out", "w", stdout);
int n; cin>>n;
for(int i=1; i<=n; i++){
string op,ad;cin>>op>>ad;
if(!check(ad)){
cout<<"ERR"<<endl;
}else{
if(op=="Server"){
if(m[ad]==0){
m[ad]=i; cout<<"OK"<<endl;
}else{
cout<<"FAIL"<<endl;
}
}else if(op=="Client"){
if(m[ad]!=0){
cout<<m[ad]<<endl;
}else{
cout<<"FAIL"<<endl;
}
}
}
}
fclose(stdin); fclose(stdout);
return 0;
}
P7912 [CSP-J 2021] 小熊的果籃 fruit
-
涉及知識點:模擬、復雜度、算法優化
-
解析:模擬題+優化
-
方法1:純模擬一遍,打好標記即可,但是會超時,能得 30~70分。
-
方法2:對方法1 進行優化,主要時間浪費在查詢上面,每一次處理過的元素其實不必再次處理了,需要用到分塊/分桶的思想,開結構體,用左右元素標記塊的界限,結合隊列的方式來實現。
-
30~70分代碼
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],cnt=0;
int main() {
freopen("fruit.in", "r", stdin);
freopen("fruit.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
while (cnt<n) {
int pre=2; // 不是 01就行
for(int i=1; i<=n; i++) {
if(a[i]!=-1 && pre!=a[i]) {
printf("%d ", i);
pre=a[i],a[i]=-1,cnt++;
}
} puts("");
}
fclose(stdin); fclose(stdout);
return 0;
}
- 這里提一下,有一個細節問題,如下程序中,使用結構體存儲數據,建議不要寫成
for(int i=1; i<=n; i++) scanf("%d", &a[i]), t[i]=T(a[i],i);
而是單獨將輸入輸出分開,可以有一定的加速。
不信可以將下面這個程序進行修改,發現會少 10分,我的天啊!竟然少了 10分。
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0;
struct T {
int value,id;
T(int a=0,int b=0):value(a),id(b) {}
} t[N];
int main() {
freopen("fruit.in", "r", stdin);
freopen("fruit.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=1; i<=n; i++) t[i]=T(a[i],i);
while(cnt<n) {
int pre=2; // 不是 01就行
for(int i=1; i<=n; i++) {
if(!vis[i] && pre!=t[i].value) {
printf("%d ",t[i].id);
vis[i]=1, ++cnt, pre=t[i].value;
}
} puts("");
}
fclose(stdin); fclose(stdout);
return 0;
}
-
分塊+隊列代碼,將每一個塊,放入隊列;每次對隊首元素進行處理。
-
該解法思路是正解,但是需要二次優化,如下程序在官方數據下跑了 80分。
點擊查看代碼
#include<cstdio>//企圖不用萬能頭,降低預編譯時間,然而並沒用。
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0;
struct T {
int l,r,value;
T(int a=0,int b=0,int c=0):l(a),r(b),value(c) {}
};
queue<T> que;
int main() {
freopen("fruit.in", "r", stdin);
freopen("fruit.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
que.push(T(1,1,a[1]));
for(int i=2; i<=n; i++) {
if(que.back().value==a[i]) que.back().r=i;
else que.push(T(i,i,a[i]));
}
while(que.size()) {
int size=que.size(), pre=2;
for(int i=1; i<=size; i++) {
if(que.front().value!=pre) {
printf("%d ", que.front().l);
que.front().l++;
pre=que.front().value;
}
if(que.front().l <= que.front().r) {
que.push(que.front());
}
que.pop();
} puts("");
}
fclose(stdin); fclose(stdout);
return 0;
}
- 滿分優化是在一個塊用完之后,出現可以合並的塊,就合並。
- 100分代碼
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0,len=0;
struct T {
int l,r,value;
T(int a=0,int b=0,int c=0):l(a),r(b),value(c) {}
} p, t;
queue<T> que, que2;
int main() {
// freopen("fruit.in", "r", stdin);
// freopen("fruit.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
que.push(T(1,1,a[1]));
for(int i=2; i<=n; i++) {
if(que.back().value==a[i]) que.back().r=i;
else que.push(T(i,i,a[i]));
}
while(cnt<n) {
while(que.size()) {
p=que.front(), que.pop();
while(vis[p.l] && p.l<=p.r) p.l++;
if(p.l>p.r) continue;
printf("%d ", p.l), cnt++;
vis[p.l]=1, p.l++;
if(p.l<=p.r) que2.push(p);
} puts("");
while(que2.size()) {
p=que2.front(), que2.pop();
while(que2.size()) {
t=que2.front();
if(p.value==t.value) p.r=t.r,que2.pop();
else break;
}
que.push(p);
}
}
return 0;
}