思路:
我们设f[i][j][0/1]表示i~j这一段节点的根节点在左边或右边的最大权值和。
只要枚举根节点k,像区间DP那样去转移就行了
c o d e code code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll MAXN = 310;
ll n, g[MAXN][MAXN], s[MAXN], f[MAXN][MAXN][2];
struct node {
ll key, val;
}a[MAXN];
ll gcd(ll x, ll y) {
if(y == 0) return x;
else return gcd(y, x % y);
}
bool cmp(node x, node y) {
return x.key < y.key;
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%lld", &n);
for(ll i = 1; i <= n; i ++)
scanf("%lld%lld", &a[i].key, &a[i].val);
memset(f, -0x3f, sizeof(f));
sort(a + 1, a + 1 + n, cmp);
for(ll i = 1; i <= n; i ++)
for(ll j = 1; j <= n; j ++) g[i][j] = gcd(a[i].key, a[j].key);
for(ll i = 1; i <= n; i ++) s[i] = a[i].val + s[i - 1];
for(ll i = 1; i <= n; i ++) {
if(i != 1 && g[i][i - 1] != 1) f[i][i][0] = a[i].val;
if(i != n && g[i][i + 1] != 1) f[i][i][1] = a[i].val;
}
ll ans = - 1e18;
for(ll l = 2; l <= n; l ++) {
for(ll i = 1; i + l - 1 <= n; i ++) {
ll j = i + l - 1;
for(ll k = i; k <= j; k ++) {
ll sum;
if(k == i) sum = f[i + 1][j][0] + s[j] - s[i - 1];
else if(k == j) sum = f[i][j - 1][1] + s[j] - s[i - 1];
else sum = f[i][k - 1][1] + f[k + 1][j][0] + s[j] - s[i - 1];
if(i != 1 && g[k][i - 1] != 1) f[i][j][0] = max(f[i][j][0], sum);
if(j != n && g[k][j + 1] != 1) f[i][j][1] = max(f[i][j][1], sum);
if(l == n) ans = max(ans, sum);
}
}
}
if(ans == - 1e18) printf("-1");
else printf("%lld", ans);
return 0;
}