有关dp的一些题目

dp(dynamic programming) 是算法竞赛中的常客

这篇博客主要用来记录一些做过dp题目

数字三角形

状态转移方程\(dp[x][y]=max(dp[x+1][y],dp[x+1][y+1])+dp[x][y]\)

#include<iostream>
using namespace std;
const int maxn=1000+10;
int n,a[maxn][maxn],dp[maxn][maxn];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;++i)
    	for(int j=1;j<=i;++j)
    		cin>>a[i][j];
    for(int i=1;i<=n;++i)
    	for(int j=1;j<=i;++j)
    		dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j];
    int ans=0;
    for(int i=1;i<=n;++i)
    	ans=max(ans,dp[n][i]);
    cout<<ans;
    return 0;
}

洛谷P1434滑雪

记忆化搜索

#include<iostream>
using namespace std;
int k;
int m,n;
int dp[10050][105],a[105][105];
int dx[4]={-1,1,0,0},dy[4]={0,0,1,-1};//l r u d
int dfs(int x,int y){
	int& ans=dp[x][y];
	if(ans>0)return ans;
	for(int i=0;i<4;++i){
		int nextx=x+dx[i],nexty=y+dy[i];
		if(nextx>=0&&nextx<m&&nexty>=0&&nexty<n)
			if(a[nextx][nexty]<a[x][y])
				ans=max(ans,dfs(nextx,nexty));
	}
	ans++;
	dp[x][y]=ans;
	return ans;
}
int main(){
	cin>>m>>n;
	for(int i=0;i<m;++i)
		for(int j=0;j<n;++j)
			cin>>a[i][j];
	int ans=0;
	for(int i=0;i<m;++i)
		for(int j=0;j<n;++j)
			ans=max(ans,dfs(i,j));
	cout<<ans;
	return 0;
}

01背包

#include<iostream>
using namespace std;
int t,m,w[105],v[105],dp[1050];
int main(){
    cin>>t>>m;
    for(int i=1;i<=m;i++)
        cin>>w[i]>>v[i];
    for(int i=1;i<=m;i++)
        for(int j=t;j>=w[i];j--)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[t];
    return 0;
}

完全背包

#include<iostream>
using namespace std;
long long dp[10000000];
int main(){
    int n,t,t1,v;
    cin>>t>>n;
    for(int i=1;i<=n;++i){
        cin>>t1>>v;
        for(int j=t1;j<=t;++j)
            dp[j]=max(dp[j-t1]+v,dp[j]);
    }
    cout<<dp[t];
    return 0;
}

洛谷P2014选课

树形dp,\(dp[i][k]\) 表示第 \(i\) 个节点选 \(k\) 个时最大价值。

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=305;
vector <int> G[maxn];
int val[maxn],dp[maxn][maxn];
int n,m;
int dfs(int u){
	dp[u][0]=0;
	for(int i=0;i<G[u].size();++i){
		int v=G[u][i];
		dfs(v);
		for(int k=m;k>=0;--k)
			for(int j=0;j<=k;++j)
				dp[u][k]=max(dp[u][k],dp[u][k-j]+dp[v][j]);
	}
	if(u!=0){
		for(int i=m;i>=1;--i)
			dp[u][i]=dp[u][i-1]+val[u];
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		int u;
		cin>>u>>val[i];
		G[u].push_back(i);
	}
	memset(dp,0xcf,sizeof(dp));
	dfs(0);
	cout<<dp[0][m];
	return 0;
} 

最大子树和

一样的树形dp,\(dp[u]\) 表示节点 \(u\) 和其子树的最大子树和,状态转移方程就是 \(dp[u]=\sum_\limits{v\in son(u)} \max (dp[v],0)\)

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=16000+50;
int n,a[maxn];
vector <int> G[maxn];
int dp[maxn];
int dfs(int x, int pa){
	int& ans=dp[x];
	ans=a[x];
	for(int i=0;i<G[x].size();i++){
		int t=G[x][i];
		if(t==pa)continue;
		dfs(t,x);
		ans+=max(dp[t],0);
	}
	return ans=max(ans,0);
}
int main() {
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	for(int i=0;i<n-1;++i) {
		int u,v;
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	int ans=0;
	for(int i=1;i<=n;++i)
		ans=max(ans,dp[i]);
	cout<<ans;
	return 0;
}
posted @ 2021-04-24 11:39  endlessloop  阅读(79)  评论(0)    收藏  举报