校内模拟赛 Label
题意:
n个点m条边的无向图,有些点有权值,有些没有。边权都为正。给剩下的点标上数字,使得$\sum\limits_{(u,v)\in E}len(u,v) \times (w[u] - w[v]) ^ 2$最小。
分析:
$$\begin{aligned}\sum_{x\to v}(w_v-w_x)^2\cdot len_v&=\sum_{x\to v}(w_v^2-2w_vw_x+w_x^2)\cdot len_v\\&=\left(\sum len_v\right)w_x^2+\left(\sum-2len_vw_v\right)w_x+\sum len_vw_v^2\end{aligned}$$
这是一个二次函数,可以知道取最小值的时候:$$w_x=\frac{\sum w_vlen_v}{\sum len_v}$$
根据这个可以列出很多个方程,然后高斯消元即可。将已确定的值放到右边,未确定的放到左边。
只与为什么一定有解,可以感性的理解一下,或者戳这
代码:
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> #include<bitset> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 505; const double eps = 1e-9; struct Edge { int to, nxt, w; } e[100005]; double A[N][N], w[N]; int a[N], b[N], c[N], head[N], En; bool B[N]; inline void add_edge(int u,int v,int w) { ++En; e[En].to = v, e[En].w = w, e[En].nxt = head[u]; head[u] = En; ++En; e[En].to = u, e[En].w = w, e[En].nxt = head[v]; head[v] = En; } inline double sqr(double x) { return x * x; } void Gauss(int n) { for (int k = 1; k <= n; ++k) { if (!B[k]) k ++; int r = k; for (int i = k + 1; i <= n; ++i) if (fabs(A[i][k]) > fabs(A[r][k])) r = k; if (r != k) for (int j = 1; j <= n + 1; ++j) swap(A[k][j], A[r][j]); if (fabs(A[k][k]) < eps) continue; for (int i = k + 1; i <= n; ++i) { if (fabs(A[i][k]) > eps) { double t = A[i][k] / A[k][k]; for (int j = 1; j <= n + 1; ++j) A[i][j] -= t * A[k][j]; } } } for (int i = n; i; --i) { if (!B[i]) continue; for (int j = i + 1; j <= n + 1; ++j) A[i][n + 1] -= A[i][j] * w[j]; w[i] = A[i][n + 1] / A[i][i]; } } int main() { int n = read(), m = read(); for (int i = 1; i <= m; ++i) { int u = read(), v = read(), w = read(); if (u != v) add_edge(u, v, w); } for (int i = 1; i <= n; ++i) w[i] = read(); for (int i = 1; i <= n; ++i) { if (w[i] < 0) { B[i] = 1; for (int j = head[i]; j; j = e[j].nxt) { int v = e[j].to; if (w[v] < 0) A[i][v] -= e[j].w; else A[i][n + 1] += 1.0 * w[v] * e[j].w; A[i][i] += e[j].w; } } } Gauss(n); double ans = 0; for (int i = 1; i <= n; ++i) for (int j = head[i]; j; j = e[j].nxt) if (e[j].to > i) ans += sqr(w[i] - w[e[j].to]) * e[j].w; printf("%.10lf\n", ans); return 0; }