tourism

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);
}

猜你喜欢

转载自blog.csdn.net/qq_32461955/article/details/82713240