srm 619 T2

来源:互联网 发布:macbookpro必装软件 编辑:程序博客网 时间:2024/04/30 13:25

题目大意:给定一棵n个节点的有根树,再给定 m 个任务(第i个任务有个花费costi),每个节点必须选择两个不同的任务。假设第i个节点选择的任务为 fi, gi,则总花费为sigma(costfi + costgi)。一种合法方案要满足的条件为:对于任意的节点i,他和他的所有儿子节点都有办法通过选择两个任务之一,使得节点i和他的所有儿子任务两两不同。求花费最少的合法方案。


主要思路:由于每个节点只影响儿子和父亲,所以很容易想到dp。显然根节点只有一个任务是有用的,而中间节点有其儿子只有一个任务是有用的。考虑fij 为 第 I 个 子树所有节点已经全部处理完毕,且第i个子树只选了任务j。接下来考虑怎么从儿子转移到父亲,由于儿子在父亲范围内不是选择J 就是 另外选一个任务 k。如果儿子选的是任务J(儿子在儿自己范围内选择了 J),则第二任务就没有用了,所以就选择一个可选的价值最低的一个。如果儿子选的是任务K,则儿子的两个任务分别是 J,K。令 w[i][j] 代表第 I 个儿子在父亲回合选择任务 J 的最小花费,这样每个儿子对应恰好一个任务,只需要做一遍二分图最小权匹配就能完成转移了。 

Ps:还是第一次见到利用权匹配的转移, 赞。


#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#define gett(x) ((x) == 0) ? 1 : 0using namespace std;const int N = 35;vector<int> son[N];int f[N][N];int w[N][N];int my[N], slack[N];int lx[N], ly[N];bool visx[N], visy[N];bool find(int x, int m) {visx[x] = 1;for (int i = 0; i < m; ++i) if (lx[x] + ly[i] == w[x][i]) {if (visy[i]) continue;visy[i] = 1;if ( my[i] == -1 || find(my[i], m)) {my[i] = x;return 1;}} else slack[i] = min(slack[i], lx[x] + ly[i] - w[x][i]);return 0;}int minvalue(int n, int m) {memset(lx, 130, sizeof(int) * n);memset(ly, 0, sizeof(int) * m);memset(my, -1, sizeof(my));for (int i = 0; i < n; ++i)for (int j = 0; j < m; ++j) {lx[i] = max(lx[i], w[i][j] = -w[i][j]);}for (int i = 0; i < n; ++i)for (memset(slack, 63, sizeof(slack));;) {memset(visx, 0, sizeof(visx));memset(visy, 0, sizeof(visy));if (find(i, m)) break;int d = 1 << 30;for (int j = 0; j < m; ++j)if (!visy[j]) d = min(d, slack[j]);for (int j = 0; j < n; ++j)if (visx[j]) lx[j] -= d;for (int j = 0; j < m; ++j)if (visy[j]) ly[j] += d;else slack[j] -= d;}int ans = 0;for (int i = 0; i < m; ++i)if (~my[i]) ans += w[my[i]][i];return -ans;}class GoodCompanyDivOne {public:int minimumCost(vector <int> superior, vector <int> training) {sort(training.begin(), training.end());int m = training.size(), n = superior.size();for (int i = 1; i < n; ++i) son[superior[i]].push_back(i);memset(f, 63, sizeof(f));for (int i = n - 1; i >= 0; --i) {if (son[i].size() + 1 > m) return -1;for (int j = 0; j < m; ++j) {int p = son[i].size();for (int ileft = 0; ileft < p; ++ileft) for (int jleft = 0; jleft < m; ++ jleft) {int &t = w[ileft][jleft];if (jleft == j) {w[ileft][jleft] = 1 << 30;continue;}t = f[son[i][ileft]][jleft] + training[gett(jleft)];for (int k = 0; k < m; ++k) if (k != jleft) t = min(t, f[son[i][ileft]][k] + training[jleft]);}f[i][j] = minvalue(p, m) + training[j];}}int ans = f[0][0] + training[1];for (int i = 1; i < m; ++i) ans = min(ans, f[0][i] + training[0]);return ans;}};


0 0
原创粉丝点击