apcs模擬考題解
by Cecill
# 1. 程式交易
### 考點: 基本語法
### 難度: ⭐
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
int n,d,x,earn,sellp,pr=-1;
int main()
{
cin>>n>>d;
for(int i=1; i<=n; i++)
{
cin>>x;
if(i==1) pr=x;
else if(pr!=-1 && x>=pr+d) earn+=x-pr,sellp=x,pr=-1;
else if(pr==-1 && x<=sellp-d) pr=x;
}
cout<<earn<<endl;
return 0;
}
```
<br>
# 2. 購物車
### 考點: 基本語法
### 難度: ⭐
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
int a,b,n,c,res,x,y;
int main()
{
cin>>a>>b>>n;
for(int i=1; i<=n; i++)
{
x=0,y=0;
while(cin>>c,c)
{
x+=(abs(c)==a)?(c/abs(c)):0;
y+=(abs(c)==b)?(c/abs(c)):0;
}
if(x && y) res++;
}
cout<<res<<endl;
return 0;
}
```
<br>
# 3. 巴士站牌
### 考點: 基本語法
### 難度: ⭐
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
int px,py,x,y,n,res,res2=INT_MAX;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>x>>y;
if(i!=1)
{
res=max(res,abs(px-x)+abs(py-y));
res2=min(res2,abs(px-x)+abs(py-y));
}
px=x,py=y;
}
cout<<res<<" "<<res2<<endl;
return 0;
}
```
<br>
# 4. 生產線
### 考點: 差分
### 難度: ⭐⭐
## 思路
1. 根據題目敘述,最終的時間花費會是 w[1]*t1 + w[2]*t2 + w[3]*t3 + .... w[n]*t4
所以我們要讓他最小,只需將最大的w[i] * 最小的 t[i] 即可 , 第二大的 * 第二小的 .... 依此類推,所以排個序即可。
2. 然後一開始要多次讓某段區間+w[i],可以想到用差分來快速處理這種操作。
複雜度O(nlogn) n為機器數量
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
long long n,m,t[N],s[N];
int main()
{
cin.tie(0),cin.sync_with_stdio(0);
cin>>n>>m;
for(int i=1; i<=m; i++)
{
int l,r,w;
cin>>l>>r>>w;
s[l]+=w,s[r+1]-=w;
}
for(int i=1; i<=n; i++) s[i]+=s[i-1];
for(int i=1; i<=n; i++) cin>>t[i];
sort(s+1,s+1+n,greater<int>());
sort(t+1,t+1+n);
long long res=0;
for(int i=1; i<=n; i++) res+=s[i]*t[i];
cout<<res<<endl;
return 0;
}
```
<br>
# 5. 贏家預測
### 考點: 基本語法
### 難度: ⭐⭐
## 思路
vwin 表示那些贏的人、vlose 表示那些輸的人、v 代表當前剩餘的人
然後依照題目敘述模擬即可
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1010;
int n,m,x;
LL att[N],exatt[N],lose[N];
vector<int> v;
vector<int> vwin,vlose;
void wcmp(int x,int y)
{
LL a=att[x],b=exatt[x],c=att[y],d=exatt[y];
if(a*b>=c*d)
{
att[x]=a+(c*d)/(2ll*b),exatt[x]=b+(c*d)/(2ll*a);
att[y]=c+c/2ll,exatt[y]=d+d/2ll;
vwin.push_back(x);
if(++lose[y]<m) vlose.push_back(y);
}
else
{
att[y]=c+(a*b)/(2*d),exatt[y]=d+(a*b)/(2*c);
att[x]=a+a/2,exatt[x]=b+b/2;
vwin.push_back(y);
if(++lose[x]<m) vlose.push_back(x);
}
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>att[i];
for(int i=1; i<=n; i++) cin>>exatt[i];
for(int i=1; i<=n; i++)
{
cin>>x;
v.push_back(x);
}
while(v.size()>1)
{
vwin.clear();
vlose.clear();
for(int i=0; i<v.size(); i+=2)
{
if(v.size()%2 && i==v.size()-1) vwin.push_back(v[i]);
else
{
int a=v[i],b=v[i+1];
wcmp(a,b);
}
}
v.clear();
for(int i=0; i<vwin.size(); i++) v.push_back(vwin[i]);
for(int i=0; i<vlose.size(); i++) v.push_back(vlose[i]);
}
cout<<v[0]<<endl;
return 0;
}
```
# 6. 動線安排
### 考點: 基本語法
### 難度: ⭐⭐⭐
## 思路
我們定義 題目中的線跟木樁以及空格為以下
直的線=1、橫的線=2、直跟橫都有=3、木樁為-1、空格為-2
所以往右或往左移除某個線 則
2 -> -2
3 -> 1
往上或往下移除某個線 則
1 -> -2
3 -> 2
所以往右或往左添加某個線 則
-2 -> 2
1 -> 3
往上或往下添加某個線 則
-2 -> 1
2 -> 3
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,t,res;
int g[N][N]; //1=| 2=- 3=+ '@'=-1 '.'=-2;
void rmvx(int x,int y)
{
g[x][y]=-2;
for(int j=y+1; j<=m; j++) //right
{
if(g[x][j]==3) g[x][j]=1;
else if(g[x][j]==2) g[x][j]=-2;
else break;
}
for(int j=y-1; j>=1; j--) //left
{
if(g[x][j]==3) g[x][j]=1;
else if(g[x][j]==2) g[x][j]=-2;
else break;
}
for(int i=x+1; i<=n; i++) //down
{
if(g[i][y]==3) g[i][y]=2;
else if(g[i][y]==1) g[i][y]=-2;
else break;
}
for(int i=x-1; i>=1; i--) //up
{
if(g[i][y]==3) g[i][y]=2;
else if(g[i][y]==1) g[i][y]=-2;
else break;
}
}
void addx(int x,int y)
{
if(g[x][y]>=1) rmvx(x,y);
g[x][y]=-1;
for(int j=y+1; j<=m; j++) //right
{
if(g[x][j]==-1)
{
for(int k=y+1; k<j; k++)
{
if(g[x][k]==-2) g[x][k]=2;
else if(g[x][k]==1) g[x][k]=3;
}
break;
}
}
for(int j=y-1; j>=1; j--) //left
{
if(g[x][j]==-1)
{
for(int k=y-1; k>j; k--)
{
if(g[x][k]==-2) g[x][k]=2;
else if(g[x][k]==1) g[x][k]=3;
}
break;
}
}
for(int i=x+1; i<=n; i++) //down
{
if(g[i][y]==-1)
{
for(int k=x+1; k<i; k++)
{
if(g[k][y]==-2) g[k][y]=1;
else if(g[k][y]==2) g[k][y]=3;
}
break;
}
}
for(int i=x-1; i>=1; i--) //up
{
if(g[i][y]==-1)
{
for(int k=x-1; k>i; k--)
{
if(g[k][y]==-2) g[k][y]=1;
else if(g[k][y]==2) g[k][y]=3;
}
break;
}
}
}
int calc()
{
int sum=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(g[i][j]==-1 || g[i][j]>=1)
sum++;
return sum;
}
int main()
{
cin>>n>>m>>t;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
g[i][j]=-2;
for(int i=1; i<=t; i++)
{
int x,y,type;
cin>>x>>y>>type;
if(type==0) addx(x+1,y+1);
else if(type==1) rmvx(x+1,y+1);
res=max(res,calc());
}
cout<<res<<"\n"<<calc();
return 0;
}
```
<br>
# 7. 基地台
### 考點: 二分搜
### 難度: ⭐⭐⭐
## 思路
1. 如果某個直徑x滿足能使每個服務點都可以得到服務則>x的直徑一定也滿足
2. 如果某個直徑x不滿足能使每個服務點都可以得到服務則<x的直徑一定也不滿足,
基於這兩點,所以可以二分
二分的check(mid)函式:
基於以下兩點,所以如果第i個服務點的座標>當前服務到的範圍,則可以貪心的在架設一個基地台到p[i]這個座標
1. 假如放在<p[i]的位置,則一定不會比放在p[i]更好
2. 放在>p[i]的位置,則第i個服務點不會被服務到
時間複雜度: O(nlogk) n為服務點數量、k為座標
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int p[N];
int n,k;
bool check(int r)
{
int cur=0,cnt=0;
for(int i=1; i<=n; i++)
if(p[i]>cur) cnt++,cur=p[i]+r;
return cnt<=k;
}
int main()
{
cin.tie(0),cin.sync_with_stdio(0);
cin>>n>>k;
for(int i=1; i<=n; i++) cin>>p[i];
sort(p+1,p+1+n);
int l=0,r=p[n];
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
```
<br>
# 8. 邏輯電路
### 考點: 拓撲排序 or DFS
### 難度: ⭐⭐⭐⭐
## 思路
根據題目敘述,為一個有向無環圖,所以對於某個點x的輸出值以及最大延遲,可以先把所有他的前驅處理完,變可求出該點,因此可以想到用拓撲排序。
1. 我們定義f[i]表示已i為終點的輸出值,delay[i]為已i為終點的最大延遲
2. 拓排過程:
2-1
首先起點為所有一開始已知的f[i],也就是那些輸入端口
2-2
當枚舉到某個邏輯閘,代表他的某個前驅點已得到,可以先用另一個陣列存起來,當所有前驅都得到(也就是入度為零時),即可得知該點的最大延遲以及輸出,此時這個點就可以用於計算更後面的點了,因次加入對列
時間複雜度: O(n+m) n為點數、m為邊數
## 參考程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=(2e3+5e4+10),M=N*2;
int p,q,r,m,f[N],delay[N],type[N],ind[N],res;
int h[N],ne[M],e[M],idx;
vector<int> v[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool calc(int type,bool a,bool b)
{
if(type==1) return a&b;
else if(type==2) return a|b;
else return a^b;
}
void topsort()
{
queue<int> qu;
for(int i=1; i<=p; i++) qu.push(i);
while(qu.size())
{
auto t=qu.front();
qu.pop();
for(int i=h[t]; ~i; i=ne[i])
{
int j=e[i];
if(j>=p+1 && j<=p+q)
{
v[j].push_back(f[t]);
delay[j]=max(delay[j],delay[t]+1);
if(--ind[j]==0)
{
if(type[j]==4) f[j]=!v[j][0];
else f[j]=calc(type[j],v[j][0],v[j][1]);
qu.push(j);
}
}
else
{
f[j]=f[t];
delay[j]=max(delay[j],delay[t]);
}
res=max(res,delay[j]);
}
}
}
int main()
{
cin.tie(0),cin.sync_with_stdio(0);
memset(h,-1,sizeof h);
cin>>p>>q>>r>>m;
for(int i=1; i<=p; i++) cin>>f[i];
for(int i=p+1; i<=p+q; i++) cin>>type[i];
for(int i=0; i<m; i++)
{
int a,b;
cin>>a>>b;
add(a,b);
ind[b]++;
}
topsort();
cout<<res<<endl;
for(int i=p+q+1; i<=p+q+r; i++) cout<<f[i]<<" ";
return 0;
}
```
<br>
# 9. 開啟寶盒
### 考點: 拓撲排序
### 難度: ⭐⭐⭐⭐
## 思路
根據題目敘述,每個寶盒都需要一些前驅點鑰匙來開啟他,且開啟寶盒後又可以獲得某些鑰匙,所以我們可以依照某種順序來模擬開啟的過程,把鑰匙跟寶盒視為圖上的點,且這樣的圖是個有向無環圖,因此可以想到拓撲排序。
1. 我們可把鑰匙跟寶盒視為圖上的點,我們假設0~n-1是寶盒 n~n+m-1是鑰匙
2. 對於某個寶盒,我們可以讓鑰匙指向他,每需要一種不同鑰匙就把他的入度+1
3. 拓排過程:
3-1
首先起點會是一開始獲得的鑰匙,也就是入度為0的點,放入對列。
3-2
對於某個鑰匙而言當枚舉到某個寶箱指向他時,即可加入隊列。
3-3
對於某個寶箱而言當入度為零表示所有它的前驅(鑰匙),都已被搜索到,即可加入隊列。
時間複雜度: O(n+m) n為點數、m為邊數
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=2*(1e5+10),M=2*5e5+10;
int n,m,k,x,t,res;
int h[N],ne[M],e[M],idx,ind[N];
bool isit[N];
vector<int> key;
//0~n-1 is box n~n+m-1 is key
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topsort()
{
queue<int> q;
for(auto i:key)
{
isit[i]=true;
q.push(i);
}
while(q.size())
{
auto t=q.front();
q.pop();
for(int i=h[t]; ~i; i=ne[i])
{
int j=e[i];
if(j>=n && !isit[j])
{
isit[j]=true;
q.push(j);
}
else if(j<n && --ind[j]==0)
{
res++;
q.push(j);
}
}
}
}
int main()
{
cin.tie(0),cin.sync_with_stdio(0);
memset(h,-1,sizeof h);
cin>>n>>m>>k;
cin>>t;
for(int i=1; i<=t; i++)
{
cin>>x;
key.push_back(x+n);
}
for(int i=0; i<n; i++)
{
for(int j=1; j<=k; j++)
{
cin>>x;
add(x+n,i);
ind[i]++;
}
}
for(int i=0; i<n; i++)
{
for(int j=1; j<=k; j++)
{
cin>>x;
add(i,x+n);
ind[x+n]++;
}
}
topsort();
cout<<res<<endl;
return 0;
}
```
<br>
# 10. 搬家
## 考點: BFS or DFS
## 難度: ⭐⭐⭐⭐⭐
## 思路
## 假如你這題是用DFS且拿到(95% RE),那你是對的,會錯是因為zerojudge的棧空間不夠用,把他等價改為BFS後你就能AC。
這題其實思路很簡單,只是實作比較複雜
1. 首先各種水管我們開個pair陣列存放水管到達的下一個點所需添加的x、y偏移量。
2. 判斷某個水管是否能接在另一個水管,我們可以在另外寫一個function,傳入(當前x,當前y,上一個x,上一個y) 來判斷方向。
3. 使用BFS模擬,當遍歷到新的可行水管時cnt++,然後繼續搜索下去,當不能搜索就return並重新計算cnt,答案就會是所有cnt(連通塊)取max。
時間複雜度: O(n*m) nm為行數和列數
```cpp
#include<bits/stdc++.h>
using namespace std;
#define F first
#define S second
typedef pair<int,int> PII;
const int N=510;
int n,m,cnt,res;
bool isit[N][N];
char g[N][N];
PII df[2]= {{0,1},{1,0}};
PII dh[2]= {{0,-1},{0,1}};
PII ds[2]= {{0,-1},{1,0}};
PII di[2]= {{-1,0},{1,0}};
PII dx[4]= {{-1,0},{1,0},{0,-1},{0,1}};
PII dl[2]= {{0,1},{-1,0}};
PII dj[2]= {{0,-1},{-1,0}};
struct point
{
int x,y,prex,prey;
};
bool valid(int x,int y)
{
if(g[x][y]=='0') return false;
if(x<=0 || x>n || y<=0 || y>m) return false;
return true;
}
int type(int x,int y,int prex,int prey)
{
if(x==prex && y<prey) return 1;//rtol
else if(x==prex && y>prey) return 2;//ltor
else if(y==prey && x<prex) return 3;//dtou
else if(y==prey && x>prex) return 4;//utod
else if(prex==-1) return -1;
return -1;
}
void bfs(int a,int b,int c,int d)
{
queue<point> q;
q.push({a,b,c,d});
while(q.size())
{
auto u=q.front();
q.pop();
int x=u.x,y=u.y,prex=u.prex,prey=u.prey;
if(isit[x][y]) continue;
int t=type(x,y,prex,prey);
bool canpass=false;
if(g[x][y]=='F' && (t==-1 || t==1 || t==3)) canpass=true;
else if(g[x][y]=='H' && (t==-1 || t==1 || t==2)) canpass=true;
else if(g[x][y]=='7' && (t==-1 || t==2 || t==3)) canpass=true;
else if(g[x][y]=='I' && (t==-1 || t==3 || t==4)) canpass=true;
else if(g[x][y]=='X') canpass=true;
else if(g[x][y]=='L' && (t==-1 || t==1 || t==4)) canpass=true;
else if(g[x][y]=='J' && (t==-1 || t==2 || t==4)) canpass=true;
if(!canpass) continue;
cnt++;
isit[x][y]=true;
if(g[x][y]=='F')
{
for(int i=0; i<2; i++)
{
int nx=x+df[i].F,ny=y+df[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='H')
{
for(int i=0; i<2; i++)
{
int nx=x+dh[i].F,ny=y+dh[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='7')
{
for(int i=0; i<2; i++)
{
int nx=x+ds[i].F,ny=y+ds[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='I')
{
for(int i=0; i<2; i++)
{
int nx=x+di[i].F,ny=y+di[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='X')
{
for(int i=0; i<4; i++)
{
int nx=x+dx[i].F,ny=y+dx[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='L')
{
for(int i=0; i<2; i++)
{
int nx=x+dl[i].F,ny=y+dl[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
if(g[x][y]=='J')
{
for(int i=0; i<2; i++)
{
int nx=x+dj[i].F,ny=y+dj[i].S;
if(!isit[nx][ny] && valid(nx,ny)) q.push({nx,ny,x,y});
}
}
}
}
int main()
{
cin.tie(0),cin.sync_with_stdio(0);
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>g[i][j];
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(!isit[i][j] && g[i][j]!='0')
{
cnt=0;
bfs(i,j,-1,-1);
res=max(res,cnt);
}
}
}
cout<<res<<endl;
return 0;
}
```