[POI2014]Tourism

题目大意:
  给定一个$n(n\le20000)$条个点,$m(m\le25000)$条边的无向图,保证图中最长路径上的点数不超过$10$。对一个点染色的代价是$w_i$。求使得每个结点都被染色或至少有一个相邻结点被染色的最小代价。

思路:
  由于图中最长路径上的点数不超过$10$,也就是说,对于每一个联通块的任一生成树,其最深结点的深度不超过$10$。考虑三进制状压DP,用$f[i][j]$表示深度为$i$的点,前$i$个深度状态为$j$的最小代价。其中每个状态的第$k$位为$0$则深度为$k$的结点已染色,若为$1$则表示未染色且相邻点也没有染色,若为$2$则表示未染色但是相邻结点有染色。搜索完一棵子树后将状态上传到父结点。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<climits>
 4 #include<algorithm>
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=20001,M=50001,K=10,S=59049;
13 const int pow[]={1,3,9,27,81,243,729,2187,6561,19683,59049};
14 bool vis[N];
15 int w[N],dep[N],h[N],sz;
16 unsigned f[K][S];
17 struct Edge {
18     int to,next;
19 };
20 Edge e[M];
21 inline void add_edge(const int &u,const int &v) {
22     e[++sz]=(Edge){v,h[u]};h[u]=sz;
23     e[++sz]=(Edge){u,h[v]};h[v]=sz;
24 }
25 inline int bit(const int &x,const int &k) {
26     return x/pow[k]%3;
27 }
28 void dfs(const int &x,const int &d) {
29     vis[x]=true;
30     if((dep[x]=d)==0) {
31         f[0][0]=w[x];
32         f[0][1]=0;
33         f[0][2]=INT_MAX;
34     } else {
35         std::fill(&f[d][0],&f[d][pow[d+1]],INT_MAX);
36         for(register int i=0,j=0;i<pow[d];j=++i) {
37             int b=1;
38             for(register int k=h[x];k;k=e[k].next) {
39                 const int &y=e[k].to;
40                 if(!vis[y]||dep[y]>=d) continue;
41                 if(bit(i,dep[y])==0) b=2;
42                 if(bit(i,dep[y])==1) j+=pow[dep[y]];
43             }
44             f[d][i+b*pow[d]]=std::min(f[d][i+b*pow[d]],f[d-1][i]);
45             f[d][j]=std::min(f[d][j],f[d-1][i]+w[x]);
46         }
47     }
48     for(int i=h[x];i;i=e[i].next) {
49         const int &y=e[i].to;
50         if(vis[y]) continue;
51         dfs(y,d+1);
52         for(register int i=0;i<pow[d+1];i++) {
53             f[d][i]=std::min(f[d+1][i],f[d+1][i+2*pow[d+1]]);
54         }
55     }
56 }
57 int main() {
58     const int n=getint(),m=getint();
59     for(register int i=1;i<=n;i++) w[i]=getint();
60     for(register int i=0;i<m;i++) {
61         add_edge(getint(),getint());
62     }
63     int ans=0;
64     for(register int i=1;i<=n;i++) {
65         if(vis[i]) continue;
66         dfs(i,0);
67         ans+=std::min(f[0][0],f[0][2]);
68     }
69     printf("%d\n",ans);
70     return 0;
71 }

 

posted @ 2018-03-29 13:35  skylee03  阅读(121)  评论(0编辑  收藏  举报