1. 程式人生 > >區間DP,子問題的深刻理解

區間DP,子問題的深刻理解

問題描述   有n個矩陣,大小分別為a0*a1, a1*a2, a2*a3, ..., a[n-1]*a[n],現要將它們依次相乘,只能使用結合率,求最少需要多少次運算。
  兩個大小分別為p*q和q*r的矩陣相乘時的運算次數計為p*q*r。 輸入格式   輸入的第一行包含一個整數n,表示矩陣的個數。
  第二行包含n+1個數,表示給定的矩陣。 輸出格式   輸出一個整數,表示最少的運算次數。 樣例輸入 3
1 10 5 20 樣例輸出 150 資料規模和約定

  1<=n<=1000, 1<=ai<=10000。

之前一直是執行錯誤,原來是陣列開大了,多看了一個0,實在是不應該,也提醒自己國賽要細心審題。

這道題可以說是我自己想出來的吧,核心思想是我自己想出來的,有幾處BUG是參照的別人的程式碼找出來的,

包括memset怎麼初始化無窮大,這就涉及16進位制了,還有就是我的INF不夠大,導致出現錯誤。

這個區間DP的程式碼我寫的很快,但就是不對,我是怎麼找都找不到,實在沒轍了我想我拿個例子模擬一下看哪裡出錯了。然後真的就讓我給找出來了。我之前的問題程式碼是這樣的:

for(i=1;i<n;i++)
	for(j=i+1;j<=n;j++)
	{
		for(k=i;k<j;k++)
		{
			dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j] + m[i].row*m[k].column*m[j].column );
		}
	}

乍一看很perfect,但我舉個例子這個寫法就暴露了我的巨大問題。比如:dp[1][4],k=1,然後就會出現

dp[1][1]+dp[2][4],請問,dp[2][4]在這之前計算出來了嗎?顯然沒有,所以說這不就錯了嘛!我為什麼會寫錯!?因為我並沒有理解什麼叫做子問題,我們在求遞推式時,等式右邊的DP陣列必須得是已經計算好了的,要不然就不對。

#include<bits/stdc++.h>
using namespace std;
const long long INF = 100000000000000000LL;
struct matrix
{
	long long row;
	long long column;
};
matrix m[1004];
int n;
long long dp[1004][1004];
int main()
{
	int i,j,k;
	cin>>n;
	int u;
	for(i=0;i<=n;i++)
	{
		cin>>u;
		m[i+1-1].column=u;
		m[i+1].row=u;
	}
	for(i=0;i<1004;i++)
	for(j=0;j<1004;j++)
	dp[i][j]=INF;
	for(i=1;i<n;i++)
	{
		dp[i][i+1]=m[i].row*m[i].column*m[i+1].column;
	}
	for(i=1;i<=n;i++)
	dp[i][i]=0;
	
	
	for(j=2;j<=n;j++)
	{
		for(i=1;i<=n;i++)
		{
			int len=i+j;
			if(len<=n)
			{
				for(k=i;k<len;k++)
				dp[i][len]=min(dp[i][len],dp[i][k] + dp[k+1][len] + m[i].row * m[k].column * m[len].column );
			}
		}
	}
	
	printf("%lld\n",dp[1][n]);
	return 0;
}