【洛谷P3577】TUR-Tourism

题目

题目链接:https://www.luogu.com.cn/problem/P3577
给定一个 \(n\) 个点,\(m\) 条边的无向图,其中你在第 \(i\) 个点建立旅游站点的费用为 \(c_i\)。在这张图中,任意两点间不存在节点数超过 \(10\) 的简单路径。
请找到一种费用最小的建立旅游站点的方案,使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。
\(n\leq 20000\)\(m\leq 25000\)

思路

首先这张图可能是不连通的,对于每一个连通块分别处理然后求和。
对于一个连通块,随便搞出一个 dfs 树,那么这个 dfs 树的深度最大为 \(10\)。且根据性质,所有不在 dfs 树上的边都是返祖边。
\(f[i][s]\) 表示跑到点 \(i\),从根节点到 \(i\) 的路径上的点状态为 \(s\)(对于一个点,\(0/1/2\) 分别表示选 / 不选且没有被覆盖 / 不选且被覆盖了),且除了 \(i\) 的祖先外,dfs 序在 \(i\) 前的点都被覆盖的最小代价。
因为如果已经处理到点 \(i\) 了,那么后面无论怎么选都只可能覆盖掉 \(i\) 祖先和 dfs 序大于 \(i\) 的点。
考虑转移。如果从父亲 \(u\) 转移到儿子 \(v\)

  • 若选 \(v\),那么与 \(v\) 相连的祖先,如果之前是 \(1\),那么需要变为 \(2\)。并且 \(v\) 这一维为 \(0\)
  • 若不选 \(v\),那么如果存在与 \(v\) 相连的祖先状态是 \(0\),那么 \(v\) 这一维为 \(2\),否则为 \(1\)

转移式的话直接看代码吧。不是很好直接写出来。
然后回溯的时候也需要更新。从儿子 \(v\) 更新到父亲 \(u\)

\[f[u][s]=\min(f[v][s],f[v][s+2\times 3^{\text{dep}[u]+1}]) \]

时间复杂度 \(O(3^{10}(n+m))\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=60010,M=12,Inf=1e9;
int n,m,tot,ans,a[N],q[M],pw[M],head[N],dep[N],f[M][N];
bool vis[N];

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void dfs(int x,int fa)
{
	vis[x]=1; dep[x]=dep[fa]+1;
	int d=dep[x];
	if (!fa) f[0][0]=a[x],f[0][1]=0,f[0][2]=Inf;
	else
	{
		int top=0;
		for (int i=head[x];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (vis[v]) q[++top]=v;
		}
		memset(f[d],0x3f3f3f3f,sizeof(f[d]));
		for (int s=0;s<pw[d];s++)
		{
			int flag=1,sta=s;
			for (int j=1;j<=top;j++)
			{
				if ((s/pw[dep[q[j]]])%3==0) flag=2;
				if ((s/pw[dep[q[j]]])%3==1) sta+=pw[dep[q[j]]];
			}
			f[d][sta]=min(f[d][sta],f[d-1][s]+a[x]);
			f[d][s+flag*pw[d]]=min(f[d][s+flag*pw[d-1]],f[d-1][s]);
		}
	}
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!vis[v])
		{
			dfs(v,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()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	pw[0]=1; dep[0]=-1;
	for (int i=1;i<M;i++) pw[i]=pw[i-1]*3;
	for (int i=1;i<=n;i++)
		if (!vis[i])
		{
			dfs(i,0);
			ans+=min(f[0][0],f[0][2]);
		}
	cout<<ans;
	return 0;
}
posted @ 2021-09-27 15:13  stoorz  阅读(100)  评论(0编辑  收藏  举报