http://www.lydsy.com/JudgeOnline/problem.php?id=1040
http://codevs.cn/problem/1423/
前言:这是第一次发bzoj题解,纪念一下。
noip在即,大家都要加油哦。
题意:有N个点,对于每个点都给出另一个点,表示它们之间有一条无向边。求最大带权独立子集。
这整个图不一定联通,但是每一个联通子图都有一个性质:边数与点数对应相等。每个联通子图可以分开处理。
最大(带权)独立子集是NP-hard问题,但是由于这里图的特殊性,可以用动态规划来解决。
对于一棵树的最大(带权)独立子集,任选一点为根,令f[i][0]和f[i][1]分别表示第i个点不选和选,该子树能取得的最大权值和。
则f[i][0]=sigma{max(f[j][0],f[j][1])},f[i][1]=w[i]+sigma{f[j][0]},其中w[i]为第i个点的权值,j是i的儿子。
对于一个边数和点数的联通图,可以考虑删去适当的一条边,使其成为一棵树。
设删掉的边的两点为x和y,分别令它们为根做树形DP,因为两者最多取其一,所以取f[x][0]和f[y][0]的较大值计入该联通子图的贡献。
代码:(拆边写的很难看,并且因为这个WA了三次,每次都90分,分别错的是第6,5,9个数据点0、
#include<cstdio>
#include<iostream>
#include<cstring>
#define rpt(i,l,r) for(i=l;i<=r;i++)
#define rpd(i,r,l) for(i=r;i>=l;i--)
#define maxn 10000005
#define mx(a,b) (a)>(b)?(a):(b)
using namespace std;
int l[maxn],w[maxn],s[maxn]={0},t[maxn<<1],b[maxn]={0},fa[maxn];
int n,i,kk,x,y;
long long f[maxn][2];
long long p,q,res=0;
void findxy(int e){
int i;
rpt(i,s[e-1]+1,s[e]) if(b[t[i]]==0&&t[i]){
b[t[i]]=1;
fa[t[i]]=e;
int tt=t[i];
findxy(t[i]);
fa[tt]=0;
}
else if((fa[e]!=t[i]||(l[l[t[i]]]==t[i]&&l[l[e]]==e))&&kk){
kk=0;
x=e;
y=t[i];
int j;
rpt(j,s[t[i]-1]+1,s[t[i]]) if(t[j]==e){
t[j]=0;
break;
}
t[i]=0;
}
}
void dp(int e){
int i;
f[e][0]=0;
f[e][1]=w[e];
rpt(i,s[e-1]+1,s[e]) if(t[i]&&fa[e]!=t[i]){
fa[t[i]]=e;
dp(t[i]);
f[e][0]+=mx(f[t[i]][0],f[t[i]][1]);
f[e][1]+=f[t[i]][0];
fa[t[i]]=0;
}
}
int main(){
scanf("%d",&n);
rpt(i,1,n){
scanf("%d%d",&w[i],&l[i]);
s[i]++;
s[l[i]]++;
}
rpt(i,1,n) s[i]+=s[i-1];
rpd(i,n,1) s[i]=s[i-1];
rpt(i,1,n){
t[++s[i]]=l[i];
t[++s[l[i]]]=i;
}
rpt(i,1,n) if(b[i]==0){
kk=1;
b[i]=1;
findxy(i);
dp(x);
p=f[x][0];
dp(y);
q=f[y][0];
res+=mx(p,q);
}
printf("%lld\n",res);
}
本文介绍了一种利用动态规划解决特定类型图的最大带权独立子集问题的方法。通过将图分解为多个联通子图,并针对每个子图构建树状结构,最终求得全局最优解。
751

被折叠的 条评论
为什么被折叠?



