首页 > 其他分享 >DP合集

DP合集

时间:2023-01-01 14:56:12浏览次数:47  
标签:cout val int cin long DP 合集 dp

被按着打

区间DP

这个应该是最基础的吧
从小阶段推到大的阶段

P1880 [NOI1995] 石子合并

经典题,一个trick是破环为链
贴个代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=205;
int dp[N][N][2],a[N],n,s[N];
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i],a[i+n]=a[i];
	int n1=n<<1;
	for(int i=1;i<=n1;++i)
		for(int j=1;j<=n1;++j) dp[i][j][1]=2e9;
	for(int i=1;i<=n1;++i) dp[i][i][0]=dp[i][i][1]=0,s[i]=s[i-1]+a[i];
	for(int len=2;len<=n;++len)
		for(int l=1,r=l+len-1;r<=n1;++l,++r)
			for(int k=l;k<r;++k)	
				dp[l][r][0]=max(dp[l][r][0],dp[l][k][0]+dp[k+1][r][0]+s[r]-s[l-1]),
				dp[l][r][1]=min(dp[l][r][1],dp[l][k][1]+dp[k+1][r][1]+s[r]-s[l-1]);
	int ans1=0,ans2=2e9;
	for(int i=1;i<=n;++i)
		ans1=max(dp[i][i+n-1][0],ans1),
		ans2=min(dp[i][i+n-1][1],ans2);
	cout<<ans2<<endl<<ans1<<endl;
}

能量项链

我们枚举此刻的长度,再进行转移就行了
方程是\(dp[i][j]=\max\limits_{i<k<j}{(dp[i][k]+dp[k][j]+a[i]\cdot a[k] \cdot a[j])}\)

P1040 [NOIP2003 提高组] 加分二叉树

由于中序遍历一定,可枚举子树根节点,转化为区间DP
这道题让我们记录最佳的状态,转移时记录即可
方程好写 Latex不好写
\(f[i][j]=\max\limits_{i<k<j}(f[i][k-1]\cdot f[k+1][j]+a[k])\)

#include<bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N=35;
int dp[N][N],n;
int root[N][N];
int a[N];
void pr(int l,int r)
{
	if(l>r) return;
	cout<<root[l][r]<<" ";
	pr(l,root[l][r]-1);
	pr(root[l][r]+1,r);
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(re int i=1;i<=n;++i) cin>>a[i];
	for(re int len=1;len<=n;++len)
	{
		for(re int l=1;l<=n-len+1;++l)
		{
			int r=l+len-1;
			dp[l][r]=max(dp[l+1][r]+a[l],dp[l][r-1]+a[r]);
			root[l][r]=(dp[l+1][r]+a[l]==dp[l][r])?l:r;
			for(re int k=l+1;k<r;++k)
			{
				if(dp[l][k-1]*dp[k+1][r]+a[k]>dp[l][r])
				{
					dp[l][r]=dp[l][k-1]*dp[k+1][r]+a[k];
					root[l][r]=k;
				}
			}
		}
	}
	cout<<dp[1][n]<<endl;
	pr(1,n);
}

P1436 棋盘分割

二维区间DP
同理,枚举两个维度的长度,转移较容易,用二维前缀和优化

#include<bits/stdc++.h>
using namespace std;
const int N=16,V=8;
int n,a[N][N],val[N][N],dp[9][9][9][9][N];
inline int ask(int a,int b,int c,int d)
{
	return val[c][d]+val[a-1][b-1]-val[a-1][d]-val[c][b-1];
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n,--n;
	memset(dp,0x3f,sizeof dp);
	for(int i=1;i<=V;++i)
		for(int j=1;j<=V;++j) 
			cin>>a[i][j],val[i][j]=val[i][j-1]+val[i-1][j]-val[i-1][j-1]+a[i][j];
	for(int i=1;i<=V;++i)
		for(int j=1;j<=V;++j)
			for(int p=i;p<=V;++p)
				for(int q=j;q<=V;++q)
					dp[i][j][p][q][0]=ask(i,j,p,q)*ask(i,j,p,q);
	for(int xlen=1;xlen<=V;++xlen)
		for(int ylen=1;ylen<=V;++ylen)
			for(int xl=1,xr=xl+xlen-1;xr<=V;++xr,++xl)
				for(int yl=1,yr=yl+ylen-1;yr<=V;++yr,++yl)
				{
					for(int p=xl;p<xr;++p) 
						for(int k=1;k<=n;++k)
							dp[xl][yl][xr][yr][k]=min(dp[xl][yl][xr][yr][k],min(dp[xl][yl][p][yr][k-1]+dp[p+1][yl][xr][yr][0],dp[xl][yl][p][yr][0]+dp[p+1][yl][xr][yr][k-1]));
					for(int p=yl;p<yr;++p) 
						for(int k=1;k<=n;++k)
							dp[xl][yl][xr][yr][k]=min(dp[xl][yl][xr][yr][k],min(dp[xl][yl][xr][p][0]+dp[xl][p+1][xr][yr][k-1],dp[xl][yl][xr][p][k-1]+dp[xl][p+1][xr][yr][0]));	
				}
	cout<<dp[1][1][V][V][n];
}

树形DP

可以看看这篇blog,讲的挺好

#10155 数字转换

我们对每个点,他的约数和和自己中,小的往大的连边(若相同便不管)
然后会形成森林,我们要做的就是求所有树中最大的直径
代码找不到了

P2015 二叉苹果树

书上背包板子

#include<bits/stdc++.h>
#define register
#define int long long
using namespace std;
const int N=105,NN=1e4+5;
int to[NN],ne[NN],f[N],tot,e[NN];
int dp[N][N];
int n,m;
inline void add(int x,int y,int z){to[++tot]=y,e[tot]=z,ne[tot]=f[x],f[x]=tot;}
inline void dp1(int x,int fa)
{
	for(int y,i=f[x];i;i=ne[i])
	{
		if((y=to[i])==fa) continue;
		dp1(y,x);
		for(int j=m;j;--j)
			for(int k=j-1;~k;--k) dp[x][j]=max(dp[x][j],dp[x][j-1-k]+e[i]+dp[y][k]);
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	int x,y,z;
	for(int i=1;i<n;++i) cin>>x>>y>>z,add(x,y,z),add(y,x,z);
	dp1(1,0);
	cout<<dp[1][m];
}

标签:cout,val,int,cin,long,DP,合集,dp
From: https://www.cnblogs.com/nich666/p/17018064.html

相关文章