传送门 (LOJ升级版)
这道题虽然是基础的区间DP,但是还是很值得一说的。
我们用dp[i][j]表示第i个点到第j个点划分的最大值。注意我们只枚举了两个端点,第三个顶点是我们枚举的那个k,之后发现k这个顶点可以把整个区间分成两块,我们就可以进行区间DP了。
只不过这道题要使用高精度。需要自己重载一下,对于赋INF值的话,我们直接把长度设为极大值即可。(好像还能用int128过)
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 10005; const ll INF = 1000000009; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct big { int f[105],len; big() { memset(f,0,sizeof(f)),len = 0; } void modi(ll g) { if(g >= INF) { len = 50; return; } while(g) f[len++] = g % 10,g /= 10; while(!f[len] && len > 0) len--; } big operator * (const big &g) const { big c; c.len = len + g.len + 1; rep(i,0,len) rep(j,0,g.len) c.f[i+j] += f[i] * g.f[j]; rep(i,0,c.len-1) c.f[i+1] += c.f[i] / 10,c.f[i] %= 10; while(!c.f[c.len] && c.len > 0) c.len--; return c; } big operator + (const big & g) const { big c; c.len = max(len,g.len) + 1; rep(i,0,c.len) c.f[i] = f[i] + g.f[i]; rep(i,0,c.len-1) c.f[i+1] += c.f[i] / 10,c.f[i] %= 10; while(!c.f[c.len] && c.len > 0) c.len--; return c; } void print() { per(i,len,0) printf("%d",f[i]);enter; } }dp[105][105],a[105]; ll n,x; big bmin(const big &a,const big &b) { if(a.len != b.len) return (a.len < b.len) ? a : b; per(i,a.len,0) if(a.f[i] != b.f[i]) return (a.f[i] < b.f[i]) ? a : b; return a; } int main() { n = read(); rep(i,1,n) x = read(),a[i].modi(x); rep(L,2,n-1) { rep(i,1,n-L) { int j = i + L; dp[i][j].modi(INF); rep(k,i+1,j-1) dp[i][j] = bmin(dp[i][j],dp[i][k] + dp[k][j] + a[i] * a[j] * a[k]); } } dp[1][n].print(); return 0; }