[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 }