Description
给定一个n个点,m条边的无向图,其中你在第i个点建立旅游站点的费用为Ci。在这张图中,任意两点间不存在节点数超过10的简单路径。请找到一种费用最小的建立旅游站点的方案,使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。
Input
第一行包含两个正整数n,m(1<=n<=20000,0<=m<=25000),分别表示点数和边数。
第二行包含n个整数,其中第i个数为Ci(0<=Ci<=10000),表示在第i个点建立旅游站点的费用。
接下来m行,每行两个正整数u,v(1<=u,v<=n),表示u与v之间连了一条边,保证没有重边。
Output
输出一行一个整数,即最小的总费用。
因为两点间不存在超过10的简单路径,也就是说一棵DFS树上的层数不超过10。
那么我们就可以状压DP乱搞了,记f[d][s]表示DFS树上第d层,树上祖先状态为s。
其中s的每一位有,0:取了 1:没取且不满足 2:没取但满足
和树上DP有区别的是,本来是DFS完儿子再合并到父亲,这是儿子影响父亲。或者递推下去,父亲影响儿子。现在是两个都来,分别用父亲和儿子来更新当前节点。
一个节点的状态会先传递给它的所有子树,然后所有子树回来更新这个节点,因为是f[d]表示层不是哪个节点,所以每次做都要重新INF啊!!!
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 20005
#define MAXN 50005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
LL res=0,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1;
res=c^48;
while(isdigit(c=getchar())) res=(res<<1)+(res<<3)+c-48;
return res*f;
}
struct EDGE{
int u,v,nxt;
}e[MAXN];
int head[maxn],cnt;
void add(int u,int v){
e[++cnt]=(EDGE){u,v,head[u]};
head[u]=cnt;
}
int n,m,POW[20];
int val[maxn],f[15][120000],dep[maxn],vis[maxn];
int ans;
int tmp[maxn];
void DFS(int u){
const int &d=dep[u]; vis[u]=1;
if(!d){
f[0][0]=val[u];
f[0][1]=0;
f[0][2]=INF;
}
else{
int tot=0;
for(int i=head[u],v;~i;i=e[i].nxt){
if(vis[v=e[i].v] && dep[v]<d) tmp[++tot]=dep[v];
}
for(int s=0;s<POW[d+1];s++) f[d][s]=INF;
for(int s=0;s<POW[d];s++){
int S=s,t=1;
for(int i=1;i<=tot;i++){
if(s/POW[tmp[i]]%3==0) t=2;
else if(s/POW[tmp[i]]%3==1) S+=POW[tmp[i]];
}
f[d][s+t*POW[d]]=min(f[d][s+t*POW[d]],f[d-1][s]);
f[d][S]=min(f[d][S],f[d-1][s]+val[u]);
}
}
for(int i=head[u],v;~i;i=e[i].nxt){
if(!vis[v=e[i].v]){
dep[v]=d+1;
DFS(v);
for(int s=0;s<POW[d+1];s++){
f[d][s]=min(f[d+1][s],f[d+1][s+2*POW[d+1]]);
}
}
}
}
int main(){
POW[0]=1;
for(int i=1;i<15;i++) POW[i]=(POW[i-1]<<1)+POW[i-1];
memset(head,-1,sizeof head);
n=read(); m=read();
for(int i=1;i<=n;i++){
val[i]=read();
}
for(int i=1,u,v;i<=m;i++){
u=read(); v=read();
add(u,v); add(v,u);
}
for(int i=1;i<=n;i++){
if(!vis[i]){
DFS(i);
ans+=min(f[0][0],f[0][2]);
}
}
printf("%d\n",ans);
}