P2607 [ZJOI2008] 骑士
题意:
有n个骑士,每个人都有自己最讨厌的骑士i(不是自己)和战力c,请从这群骑士中选择一群骑士,使这里的每个骑士都不会遇到最讨厌的骑士且战力总和最大。(别人都打过来了还挑三拣四)
思路:
初一看,哇!没有上司的舞会,送分题啊!再一看,似乎有点不对,好像没有根root?
这题有n个点n条边,也就是说,这是一个有向有环图(*+﹏+*)~,那么,如何处理那个环就是最大的问题了
比如样例
这种情况下肯定不能直接树形DP(不存在根),考虑删去一条边。
现在能进行树形DP了,记f[x][0]为现在处理x时不选,f[x][1]为现在处理x时选。
定义若x讨厌y,则x为y的父节点jie
void dp (int x) {//vd[x] -> whether we deal with x or not f[x][0/1] -> dp memory
vd[x] = 1;
f[x][0] = 0;
f[x][1] = att[x];
for (int i = 0; i < fa[x].size(); i++) {
int y = fa[x][i];
if (!vd[y]) dp(y);
f[x][0] += max(f[y][0],f[y][1]);
f[x][1] += f[y][0];
}
}
接下来的问题就是找环了,因为每个人只有一个讨厌的人,所以可以按这条路看成一个链表,记v[x]表示搜过了,如果发现在处理v[x]之前v[x] = true,表明已经发现了环的一个点。
void dfs (int x) {
if (v[x]) {
return;
}
v[x] = true;
dfs(son[x]);
}
接下来就是dp,dfs和f数组之间的联系了,找到v[x]后,root = x,此时删除root - son[root]的这条边,唯一限制条件就是root与son[root]二者最多只能选其一(可以都不选),则答案为max(f[root][0],f[son[root]][0]) 。因为对于root = x,root = son[x]根不同,所对应的树结构类似但权限不同,所以需要处理dp(x)和dp(son[x])
void dp (int x) {
vd[x] = 1;
v[x] = 1;
f[x][0] = 0;
f[x][1] = att[x];
for (int i = 0; i < fa[x].size(); i++) {
int y = fa[x][i];
if (y == root) {
side = x;
continue;
}
if (!vd[y]) dp(y);
f[x][0] += max(f[y][0],f[y][1]);
f[x][1] += f[y][0];
}
}
void dfs (int x) {
if (v[x]) {
root = x;
long long t = 0;
dp(x);
t = max(f[root][0],f[side][0]);
root = side;
memset(f,0,sizeof(f));
memset(vd,0,sizeof(vd));
dp(root);
res += max(t,max(f[root][0],f[side][0]));
return;
}
v[x] = true;
dfs(son[x]);
}
最后在主函数里while循环1~n找环,完结撒花
//AC代码
#include
#include
#define maxn 1000005
using namespace std;
int n;
vector fa[maxn];
int son[maxn];
int att[maxn];
bool v[maxn] = {false};
long long res = 0;
long long f[maxn][2];
int root;
int side;
bool vd[maxn] = {false};
void dp (int x) {
vd[x] = 1;
v[x] = 1;
f[x][0] = 0;
f[x][1] = att[x];
for (int i = 0; i < fa[x].size(); i++) {
int y = fa[x][i];
if (y == root) {
side = x;
continue;
}
if (!vd[y]) dp(y);
f[x][0] += max(f[y][0],f[y][1]);
f[x][1] += f[y][0];
}
}
void dfs (int x) {
if (v[x]) {
root = x;
long long t = 0;
dp(x);
t = max(f[root][0],f[side][0]);
root = side;
memset(f,0,sizeof(f));
memset(vd,0,sizeof(vd));
dp(root);
res += max(t,max(f[root][0],f[side][0]));
return;
}
v[x] = true;
dfs(son[x]);
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> att[i] >> son[i];
fa[son[i]].push_back(i);
}
int i = 1;
while (i <= n) {
if (!v[i]) {
dfs(i);
}
i++;
}
cout << res << endl;
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)