1. 程式人生 > 實用技巧 >2020.10.18:YC中學模擬賽

2020.10.18:YC中學模擬賽

2020.10.18:YC中學模擬賽
(難度介於CSP-J2~CSP-S2)

紀念品分組

傳送門

講解

水題,但寫sort人傻了,忘了sort是左閉右開的結構,導致陣列沒排完序 (分數-40)

題意就是將N件物品2個一組,讓求組數最少時,每組價值差儘量小
具體怎麼做呢?

考試的時候,我是一開始想到的是用最小价值的物品和最大價值的物品相加,次小和次大相加,第K小的和第K大的相加,這樣必定每組價值差最小

sort(a,a+n+1);
int l=1,r=n;
while(l<=r){
	l++;
	r--;
	ans++;
}

但如果某兩數之和大於價值上限怎麼辦?
所以進行判斷,單獨取出這兩個數中較大的一個,獨成一組;而較小的一個參與與其他數字的合併

這是滿足貪心的思想的

程式碼

#include<bits/stdc++.h>
using namespace std;
int mx;//每組紀念品價格之和的上限
int n;//購來的紀念品的總件數
int a[30100];
int ans;
 
int main(){
	//freopen("souvenir.in","r",stdin);
	//freopen("souvenir.out","w",stdout);
	cin>>mx>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	sort(a,a+n+1);
	int l=1,r=n;
	while(l<=r){
		if(a[l]+a[r]>mx){
			r--;
			ans++;
		}else{//a[l]+a[r]<=mx
			l++;
			r--;
			ans++;
		}
		
	}
	cout<<ans;
	return 0;
}

開閉區間

順便插一句,sort一類的STL都是左閉右開的結構

左閉右開:一種區間結構
( 開區間 ):區間兩處的端點值取不到
[ 閉區間 ]:區間兩處的端點值可以取到

所以,我就是忘了sort是左閉右開的那個人,然後就給下標0~n-1的資料排序,唯獨沒到下標n

sort(a,a+n);

城市互通

傳送門

講解

就是有N個點M條邊,然後看這些點邊構成了多少個集合,最後-1(看需要連多少條邊)就好了
我想的是用並查集來做 (就構建集合,然後查詢,比並查集模板還簡單

也沒什麼說的,就將每個點掛到自己的根節點上
最後隨便數一下有多少個根節點就完成

不會並查集的同學請看看這篇文章:【並查集】一種與時間賽跑的巧妙演算法


以及學習並查集請必需掌握的鏈式前向星:關於路徑儲存的常見優化——前向星與鏈式前向星

程式碼

#include<bits/stdc++.h>
using namespace std;

int n,m;//N個城,M條路 
int fa[100100];

int find(int i){
	if(i==fa[i]){//根節點 
		return i;
	}
	else{
		int x=find(fa[i]);
		fa[i]=x;
		return x;
	}
}
int main(){
	//freopen("city.in","r",stdin);
	//freopen("city.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;//x -- y
		
		fa[find(x)] = find(y);
		
	}
	long long ans=0;
	for(int i=1;i<=n;i++){
		if(find(i)!=find(i-1)){
			fa[find(i)]=find(i-1);
			ans++;
		}
	}
	ans--;
	cout<<ans;
	
	return 0;
}

遊蕩的奶牛

傳送門

講解

就是一張NM的地圖,為障礙物,求奶牛T步內從[sx,sy]到[ex,ey]的方案數

DFS

我最開始打了個DFS搜尋,以為T<=15資料能過

#include<bits/stdc++.h>
using namespace std;
int n,m,t;
char a[110][110];
int sx,sy,ex,ey;

int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

bool vis[110][110];

int ans;

bool in(int x,int y){
	return 0<x&&x<=n&&0<y&&y<=m;
}

void dfs(int x,int y,int z){//[x,y],第z秒 
	if(z>t)return;
	if(x==ex&&y==ey){
		ans++;
	}
	//cout<<x<<" "<<y<<endl;
	for(int i=0;i<4;i++){
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(in(tx,ty)&&vis[tx][ty]==0&&a[tx][ty]!='*'&&z<t){
			vis[tx][ty]=1;
			dfs(tx,ty,z+1);
		}
	}
	vis[x][y]=0;
	return;
}

int main(){
	//freopen("cow.in","r",stdin);
	//freopen("cow.out","w",stdout);
	cin>>n>>m>>t;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	} 
	cin>>sx>>sy>>ex>>ey;
	dfs(sx,sy,0);
	cout<<ans;
	return 0;
}

下來大佬YJA告訴我,這樣時間複雜度高達約O(4^K)(4個方向,可以走T步,最壞約為10 ^9)

所以我們進行記憶化

以及,我們還要考慮目前所在的點和終點間的距離(這裡用曼哈頓距離進行估價,即|X1-X2|+|Y1-Y2|) 是否大於T-走過的步數
(即:|X1-X2|+|Y1-Y2|<=t-z)
若大於那麼從起點走T次後一定無法到達終點,所以後面一切均為無用功,直接結束本次搜尋(可行性剪枝)

DP

可以用記憶化就可能能用DP

DP有個好處就是可以不進行可行性剪枝
而改用三維陣列,某一維記錄當前步數

在T步內,到達[i,j]方案數為上下左右除去障礙物的和

然後判斷是否越界等基本操作,給f[sx,sy]賦初值=1

f[i][j][k] 表示第k時間到達(i,j)的方案數,得到:f[x][y][k]+=f[x+dir[0][i]] [y+dir[1][i]] [k+1]

//dir表方向變化

程式碼

DP

#include<bits/stdc++.h>
using namespace std;
int n,m,t;
int sx,sy,ex,ey;
char a[110][110];
int f[110][110][110];
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
bool in(int x,int y) {
	return 0<x&&x<=n&&0<y&&y<=m;
}

int main() {
	cin>>n>>m>>t;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			cin>>a[i][j];
		}
	}
	cin>>sx>>sy>>ex>>ey;
	f[sx][sy][0]=1;
	for(int i=1; i<=t; i++) {
		for(int j=1; j<=n; j++) {
			for(int k=1; k<=m; k++) {
				if(a[j][k]!='*') {
					for(int l=0; l<4; l++) {
						int tx=j+dir[l][0];
						int ty=k+dir[l][1];
						if(in(tx,ty)&&a[tx][ty]!='*') {
							f[j][k][i]+=f[tx][ty][i-1];
						}
					}
				}
			}
		}
	}

	cout<<f[ex][ey][t]<<endl;
//	for(int k=1; k<=t; k++) {
//		for(int i=1; i<=n; i++) {
//			for(int j=1; j<=m; j++) {
//				cout<<f[i][j][k]<<" ";
//			}
//			cout<<endl;
//		}
//		cout<<endl;
//	}
	return 0;
}

理髮

傳送門
線段樹,暫時不太懂
略......
考試的時候暴力,A了兩組資料,其他超時

//A兩組資料
#include<bits/stdc++.h>
using namespace std;
long long n;
long long a[10100];
long long ans;
int main() {
	//freopen("haircut.in","r",stdin);
	//freopen("haircut.out","w",stdout);
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	cout<<0<<endl;
	for(int k=1; k<n; k++) {
		ans=0;
		for(int i=1; i<=n; i++) {
			for(int j=1; j<i; j++) {
				int x=a[i];
				int y=a[j];
				if(x>k)x=k;
				if(y>k)y=k;
				if(y>x){
					ans++;
					//cout<<ans<<" ";
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}