1. 程式人生 > >區間dp【洛谷P1040】

區間dp【洛谷P1040】

照例送上題目連結:https://www.luogu.org/problemnew/solution/P1040

這個題目是個區間dp(為什麼會在洛谷的dfs裡面,我也不知道啊)

dfs啊呸,區間dp,顧名思義,就是在一段區間[l,r]上的dp(我在說什麼亂七八糟的,劃掉劃掉)。

乍一看這個題目,看似無從下手(可能只有我自己無從下手),其實仔細一分析,題目要,求最大值,我們就直接列舉樹根,然後把左右兒子按照題目要求的操作一波就好啦。

dp方程直接給出來了,很好理解  dp[i][j] = max(dp[i][j],dp[i][k-1]*dp[k+1][j]+dp[k][k]) ,其中dp[i][j]表示節點i到節點j的最大值。

 

1.首先列舉區間長度。

2.列舉左端點。

3.列舉根

先序遍歷就直接遞迴輸出root就好啦(先訪問根,然後輸出左邊,然後輸出右邊)。

還是直接看程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50;
ll dp[maxn][maxn];
ll root[maxn][maxn];
void init()
{
	memset(dp,0,sizeof(dp));
	memset(root,0,sizeof(root));
}
void print(int l,int r)
{
	if(l>r)
	{
		return;
	}
	cout<<root[l][r]<<" ";
	if(l==r)
	{
		return;
	}
	print(l,root[l][r]-1);
	print(root[l][r]+1,r);
}
int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
		{
			cin>>dp[i][i];
			dp[i][i-1] = 1;
			root[i][i] = i;
		}
		for(int len = 1;len<n;len++)
		{
			for(int i=1;i+len<=n;i++)
			{
				int j = i+len;
				dp[i][j] = dp[i+1][j]+dp[i][i];
				root[i][j] = i;
				for(int k=i+1;k<j;k++)
				{
					if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+dp[k][k])
					{
						dp[i][j]=dp[i][k-1]*dp[k+1][j]+dp[k][k];
						root[i][j] = k;
					}
				}
			}
		}
		cout<<dp[1][n]<<endl;
		print(1,n);
	}
	return 0;
}

PS:等等,這是個樹形dp?