區間DP,子問題的深刻理解
阿新 • • 發佈:2019-02-14
問題描述
有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 資料規模和約定
乍一看很perfect,但我舉個例子這個寫法就暴露了我的巨大問題。比如:dp[1][4],k=1,然後就會出現
兩個大小分別為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; }