題目描述:
給定一個具有 N 個頂點的凸多邊形,將頂點從 1 至 N 標號,每個頂點的權值都是一個正整數。
將這個凸多邊形劃分成 N−2個互不相交的三角形,對於每個三角形,其三個頂點的權值相乘都可得到一個權值乘積,試求所有三角形的頂點權值乘積之和至少爲多少。
輸入格式
第一行包含整數 N,表示頂點數量。
第二行包含 N 個整數,依次爲頂點 1 至頂點 N 的權值。
輸出格式
輸出僅一行,爲所有三角形的頂點權值乘積之和的最小值。
數據範圍
N≤50,
數據保證所有頂點的權值都小於10^9
輸入樣例:
5
121 122 123 245 231
輸出樣例:
12214884
分析:
首先需要考慮如何將本題劃分爲若干個相似的子問題。
如上圖所示,一個編號爲1到6的六邊形,我們考慮劃分時邊16會被哪個三角形選中,頂點1,6構成的三角形可以是126,136,146,156。比如隨便選取其中的一個三角形146,原圖形就被劃分爲了三部分,最簡單的藍色三角形146,以及藍色三角形上面的圖形和下面的圖形,由於劃分的三角形不能交叉,所以不會存在像125這樣會與146交叉的三角形,也就是說,這三部分,任意一部分都是一個獨立的子問題。我們考慮了由頂點1和N構成的三角形後,剩下的兩部分都是與原問題相同但是規模小於原問題的獨立子問題,這就類似於石子合併問題,找到到中間藍色三角形的代價,然後加上上下部分代價就是原問題的代價了。即f[i][j]表示頂點編號i到j多邊形中所有三角形權值乘積之和的最小值,狀態轉移方程爲f[i][j] = min(f[i][j],f[i][k]+f[k][j] + w[i]*w[j]*w[k])。本來是道簡單的區間DP問題,但是數據範圍較大,需要用高精度實現加法和乘法運算。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int N = 55;
int a[N];
vi f[N][N];
void add(vi& x,vi& y,vi& z){
if(x.size() < y.size()) add(y,x,z);
else{
int t = 0;
for(int i = 0;i < x.size();i++){
t += x[i];
if(i < y.size()) t += y[i];
z.push_back(t % 10);
t /= 10;
}
if(t) z.push_back(t);
}
}
void mul(vi& x,ll y,vi& z){
ll t = 0;
for(int i = 0;i < x.size() || t;i++){
if(i < x.size()) t += x[i] * y;
z.push_back(t % 10);
t /= 10;
}
}
bool cmp(vi& x,vi& y){
if(x.size() != y.size()) return x.size() < y.size();
int i = x.size() - 1;
while(i && x[i] == y[i]) i--;
return x[i] < y[i];
}
int main(){
int n;
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
for(int len = 3;len <= n;len++){
for(int l = 1;l+len-1<= n;l++){
int r = l + len -1;
for(int k = l + 1;k < r;k++){
vi res,s,t(1,a[l]);
mul(t,a[r],s),mul(s,a[k],res);
s.clear(),t.clear();
add(f[l][k],f[k][r],s),add(s,res,t);
if(f[l][r].empty() || cmp(t,f[l][r])) f[l][r] = t;
}
}
}
for(int i = f[1][n].size() - 1;i >= 0;i--) cout<<f[1][n][i];
return 0;
}