[題解](A-G)Atcoder Educational DP Contest(更新中)

Sinktank發表於2024-04-14

Atcoder Educational DP Contest

\(\textbf{A. Frog 1}\)

對於一塊石頭\(i(3 \le i \le N)\)\(i-1\)\(i-2\)均能到達。

\(f[i]\)表示跳到第\(i\)個石頭用的最小體力消耗:

\[f[i]=min(abs(h[i]-h[i-1])+f[i-1],abs(h[i]-h[i-2])+f[i-2])\qquad i\ge 3 \]

時間複雜度\(O(n)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,h[200010],dp[200010];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>h[i];
	dp[2]=abs(h[2]-h[1]);
	for(int i=3;i<=n;i++){
		dp[i]=min(abs(h[i-1]-h[i])+dp[i-1],abs(h[i-2]-h[i])+dp[i-2]);
	}
	cout<<dp[n];
	return 0;
}

\(\textbf{B. Frog 2}\)

與上一題類似,不過這一題每個石頭都和前\(K\)個相關。

時間複雜度\(O(nk)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,k;
int h[100010],dp[100010];
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>h[i];
	for(int i=2;i<=k;i++){
		int minn=1e9+7;
		for(int j=1;j<i;j++){
			minn=min(minn,dp[j]+abs(h[j]-h[i]));
		}
		dp[i]=minn;
	}
	for(int i=k+1;i<=n;i++){
		int minn=1e9+7;
		for(int j=1;j<=k;j++){
			minn=min(minn,dp[i-j]+abs(h[i-j]-h[i]));
		}
		dp[i]=minn;
	}
	cout<<dp[n];
	return 0;
}

\(\textbf{C. Vacation}\)

\(f[i][j]\)表示進行到第\(i\)天,這一天做了事件\(j\)的最大快樂值。列舉\(i,j\),對於每一個\(j\)再列舉昨天的事件\(k(k\neq j)\),求最大即可。

時間複雜度\(O(n)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,a[100010][3];
int dp[100010][3];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++){
			cin>>a[i][j];
		}
	}
	for(int i=0;i<3;i++) dp[1][i]=a[1][i];
	for(int i=2;i<=n;i++){
		for(int j=0;j<3;j++){
			for(int k=0;k<3;k++){
				if(k==j) continue;
				dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i][j]);
			}
		}
	}
	cout<<max(max(dp[n][0],dp[n][1]),dp[n][2]);
	return 0;
}

\(\textbf{D. Knapsack 1}\)

01揹包問題,注意開long long

時間複雜度\(O(nw)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,m,w[110],v[110];
long long dp[100010];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=m;j>=w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}
	cout<<dp[m];
	return 0;
}

\(\textbf{E. Knapsack 2}\)

與D相似,但是我們發現資料範圍上有差異:

  • 上一題\(1 \leq W \leq 10^5,1 \leq v_i \leq 10^9\)
  • 這一題\(1 \leq W \leq 10^9,1 \leq v_i \leq 10^3\)

所以如果用\(O(nw)\)會超時,怎麼利用物品價值範圍小這一特點最佳化呢?

我們用\(f[i][j]\)表示選前\(i\)個物品,總價值為\(j\)的揹包最小容量。

如果仔細想想,就能發現這轉化成了\(n\)個物品,揹包容量為\(1\sim N*v(10^5)\),把價值當作每個物品的體積,求最小价值(這裡就是原條件的體積了)的01揹包正好裝滿問題。我們知道,正好裝滿的揹包問題和普通揹包問題就差在一個初始化,因為我們求的是最小值,所以把\(f\)初始化為極大值就可以啦~

最後從大到小列舉每個揹包容量\(i\),如果\(f[i]\leq m\),則直接輸出\(i\)即可。

時間複雜度\(O(n^2v)\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,m,w[110],v[110];
int dp[100010];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	memset(dp,0x3f,sizeof dp);
	dp[0]=0;
	for(int i=1;i<=n;i++){
		for(int j=100000;j>=v[i];j--){
			dp[j]=min(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	for(int i=100000;i>=1;i--){
		if(dp[i]<=m){
			cout<<i;
			return 0;
		}
	}
	return 0;
}

\(\textbf{F. LCS}\)

最長公共子序列模板題,動態規劃的線性模型一文中已經有詳解了,所以就不再贅述了。

時間複雜度\(O(n^2)\)\(n\)是字串長度)。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
string a,b;
int n,m,f[3010][3010];
char d[3010][3010];
int main(){
	cin>>a>>b;
	n=a.size(),m=b.size();
	a=' '+a,b=' '+b;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i]==b[j]){
				d[i][j]='+';
				f[i][j]=f[i-1][j-1]+1;
			}else{
				if(f[i-1][j]>f[i][j-1]){
					d[i][j]='U';
					f[i][j]=f[i-1][j];
				}else{
					d[i][j]='L';
					f[i][j]=f[i][j-1];
				}
			}
		}
	}
	string ans="";
	for(int x=n,y=m;x>0&&y>0;){
		if(d[x][y]=='+'){
			ans+=a[x];
			x--,y--;
		}else if(d[x][y]=='U'){
			x--;
		}else{
			y--;
		}
	}
	reverse(ans.begin(),ans.end());
	cout<<ans;
	return 0;
}

\(\textbf{G. Longest Path}\)

記搜即可,最後搜出的點數\(-1\)就是邊數。

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> p[100010];
bool b[100010];
int mem[100010];
int dfs(int pos){
	//記憶化
	if(mem[pos]) return mem[pos];
	int len=p[pos].size();
	int maxx=0;
	for(int i=0;i<len;i++){
		maxx=max(maxx,dfs(p[pos][i]));
	}
	return mem[pos]=maxx+1;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		p[y].push_back(x);
		b[x]=1;
	}
	int maxx=-1;
	for(int i=1;i<=n;i++){
		if(!b[i]){
			maxx=max(maxx,dfs(i));
		}
	}
	cout<<maxx-1;//邊=點-1
	return 0;
}

相關文章