CodeForces 1666J Job Lookup
思路
考虑区间 dp。设 \(f_{i,j}\) 为只考虑区间 \([l,r]\) 的点的最小值。转移就考虑计算每条边的贡献,枚举根,则根到左子树的这条边的贡献即为左子树的点为 \(i\),非左子树的点为 \(j\) 的所有 \(c_{i,j}\) 的和。右子树同理。二维前缀和预处理一下即可做到 \(O(n^3)\)。
需要记录路径。
代码
code
/*
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int maxn = 210;
ll n, a[maxn][maxn], f[maxn][maxn], pre[maxn][maxn];
ll sum[maxn][maxn];
int g[maxn];
ll query(int xa, int ya, int xb, int yb) {
	if (xa > xb || ya > yb) {
		return 0;
	}
	return sum[xa - 1][ya - 1] + sum[xb][yb] - sum[xa - 1][yb] - sum[xb][ya - 1];
}
void dfs(int l, int r, int fa) {
	if (l > r) {
		return;
	}
	g[pre[l][r]] = fa;
	dfs(l, pre[l][r] - 1, pre[l][r]);
	dfs(pre[l][r] + 1, r, pre[l][r]);
}
void solve() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			scanf("%lld", &a[i][j]);
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
		}
	}
	memset(f, 0x3f, sizeof(f));
	for (int i = 1; i <= n; ++i) {
		f[i][i] = 0;
		pre[i][i] = i;
	}
	for (int i = 1; i <= n + 1; ++i) {
		f[i][i - 1] = 0;
	}
	for (int p = 2; p <= n; ++p) {
		for (int i = 1, j = p; j <= n; ++i, ++j) {
			for (int k = i; k <= j; ++k) {
				// 此时 k 为树根
				ll res = f[i][k - 1] + f[k + 1][j];
				res += query(i, 1, k - 1, i - 1) + query(i, k, k - 1, n);
				res += query(k + 1, 1, j, k) + query(k + 1, j + 1, j, n);
				if (res < f[i][j]) {
					f[i][j] = res;
					pre[i][j] = k;
				}
			}
		}
	}
	int rt = pre[1][n];
	dfs(1, rt - 1, rt);
	dfs(rt + 1, n, rt);
	for (int i = 1; i <= n; ++i) {
		printf("%d ", g[i]);
	}
}
int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号