zoukankan      html  css  js  c++  java
  • [洛谷P3569] POI2014 KAR

    问题描述

    给定一个n个点,m条边的无向图,其中你在第i个点建立旅游站点的费用为Ci。在这张图中,任意两点间不存在节点数超过10的简单路径。请找到一种费用最小的建立旅游站点的方案,使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。

    解析

    对于这类问题,不妨对原图的每个连通块建出 DFS 树,那么原问题就转化为了树上点覆盖问题。由题目的性质,这棵树的深度不会超过10。

    我们用一个3进制数表示一个点的状态:0表示这个点被选择,1表示这个点没有被选也没有被覆盖,2表示这个点没有被选但是被覆盖了。设 (f_{u,d,s}) 表示当前在点 (u) ,深度为 (d)(u) 的祖先们状态为 (s) 时的最小代价。转移时先考虑 (u) 的返祖边,然后继续DP,再用子树的状态更新 (f_{u,d}) 。在实际实现中 (u) 这一维可以省略。

    设连通块的 DFS 树根节点为 (root),那么答案为 (sum min(f_{root,0,0},f_{root,0,2}))

    代码

    #include <iostream>
    #include <cstdio>
    #define N 20002
    #define M 25002
    #define S 60000
    using namespace std;
    const int inf=1<<30;
    int head[N],ver[M*2],nxt[M*2],l;
    int n,m,i,w[N],c[N],f[12][N],pw[12],a[N],dep[N],ans;
    bool vis[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y)
    {
    	l++;
    	ver[l]=y;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void dfs(int x,int pre)
    {
    	vis[x]=1;
    	int cnt=0,d=dep[x];
    	if(d==0) f[0][0]=w[x],f[0][1]=0,f[0][2]=inf;
    	else{
    		for(int i=head[x];i;i=nxt[i]){
    			int y=ver[i];
    			if(vis[y]&&dep[y]<d) a[++cnt]=dep[y];
    		}
    		for(int s=0;s<pw[d+1];s++) f[d][s]=inf;
    		for(int s=0;s<pw[d];s++){
    			int s1=s,op=1;
    			for(int i=1;i<=cnt;i++){
    				if(s/pw[a[i]]%3==0) op=2;
    				else if(s/pw[a[i]]%3==1) s1+=pw[a[i]];
    			}
    			f[d][s+op*pw[d]]=min(f[d][s+op*pw[d]],f[d-1][s]);
    			f[d][s1]=min(f[d][s1],f[d-1][s]+w[x]);
    		}
    	}
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(!vis[y]){
    			dep[y]=dep[x]+1;
    			dfs(y,x);
    			for(int s=0;s<pw[d+1];s++) f[d][s]=min(f[d+1][s],f[d+1][s+2*pw[d+1]]);
    		}
    	}
    }
    int main()
    {
    	n=read();m=read();
    	for(i=1;i<=n;i++) w[i]=read();
    	for(i=1;i<=m;i++){
    		int u=read(),v=read();
    		insert(u,v);insert(v,u);
    	}
    	for(i=pw[0]=1;i<=10;i++) pw[i]=pw[i-1]*3;
    	for(i=1;i<=n;i++){
    		if(!vis[i]){
    			dfs(i,0);
    			ans+=min(f[0][0],f[0][2]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Android 交错 GridView
    Android 从 Android 本地图库选择多个图片
    Android 布局管理器
    Android 高级 Jackson Marshalling(serialize)/Unmarshalling(deserialize)
    Android 基本 Jackson Marshalling(serialize)/Unmarshalling(deserialize)
    Android Jackson 概述
    Andorid 翻书效果
    Android 原生 Android ActionBar Tab (滑动)导航
    Android 原生 Android ActionBar
    Android 关于操作栏 ActionBar 的设计原则【转载+整理】
  • 原文地址:https://www.cnblogs.com/LSlzf/p/13912278.html
Copyright © 2011-2022 走看看