Uva-12093 Protecting Zonk(较复杂的树形DP)

185 篇文章 0 订阅
116 篇文章 0 订阅

题意:给定一个有n(n <= 10000) 个节点的无根树.有两种装置A和B,每种都有无线多个.

在某个节点x使用A装置需要C1(C1 <= 1000)的花费,并且此时与节点x相连的边都被覆盖.

在某个节点X使用B装置需要C2(C2 <= 1000)的费用,并且此时与节点x相连的边以及与节点x相连的点相连的边都被覆盖.

求覆盖所有边的最小花费.


分析:状态有很多种表示方法,f[u][a][b][c] 表示第i个点选了a装置,他父亲选了b装置,c表示它的儿子中是否有选C2装置,或者f[u][a][b][c]表示第i个点选了a装置,他父亲选了b装置,c表示u到fa[u]的边是否被u上边的点覆盖,这两种状态定义方法都能转移,其实这道题的状态表示方法不难想到,难就在这个状态猛一看很多,给人一种无从下手的感觉,其实手动列一下这18个状态就会发现它们的转移其实一共就三种类型.



第二种状态表示方法:

#include <bits/stdc++.h>
#define N 10005
#define INF 1047483647
#define MOD 1000000007
using namespace std;
int n,u,v,c1,c2,f[N][3][3][2],dp[N][2];
vector<int> G[N];
void dfs(int u,int fa)
{
	for(int v : G[u])
	 if(v != fa) dfs(v,u);
	for(int i = 0;i < 3;i++)
	 for(int j = 0;j < 3;j++)
	  for(int k = 0;k < 2;k++)
	  {
	      if(!k && j) continue;
	      f[u][i][j][k] = !i ? 0:(i == 1 ? c1 : c2);  
	  	  int t = 0,V_tot = 0;
	  	  dp[0][1] = INF; 
		  for(int v : G[u])	
		  if(v != fa)
		  {
		  	 t++;
		  	 V_tot += min(min(f[v][0][i][i || (j==2)],f[v][1][i][i || (j==2)]),f[v][2][i][i || (j==2)]);
		 	 dp[t][0] = dp[t-1][0] + min(f[v][0][i][1],f[v][1][i][1]);
		  	 dp[t][1] = min(dp[t-1][1] + min(f[v][2][i][1],min(f[v][0][i][1],f[v][1][i][1])),dp[t-1][0] + f[v][2][i][0]);
		  }
		  if(!(i+j+k)) f[u][i][j][k] += dp[t][1];
		  else 
		   if(!i && j < 2) f[u][i][j][k] += min(dp[t][1],V_tot);
		   else f[u][i][j][k] += V_tot; 
	  }
}
int main()
{
	while(~scanf("%d%d%d",&n,&c1,&c2) && (n+c1+c2))
	{
		for(int i = 1;i <= n;i++) G[i].clear();
		for(int i = 1;i < n;i++)
		{
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		dfs(1,0);
		cout<<min(min(f[1][0][0][1],f[1][1][0][1]),f[1][2][0][1])<<endl;
	}
}

第一种状态表示方法:


#include <bits/stdc++.h>
#define N 10005
#define INF 1047483647
#define MOD 1000000007
using namespace std;
int n,u,v,c1,c2,f[N][3][3][2],dp[N][2];
vector<int> G[N];
void dfs(int u,int fa)
{
	for(int v : G[u])
	 if(v != fa) dfs(v,u);
	for(int i = 0;i < 3;i++)
	 for(int j = 0;j < 3;j++)
	  for(int k = 0;k < 2;k++)
	  {
	  	  f[u][i][j][k] = !i ? 0:(i == 1 ? c1 : c2);  
	  	  int t = 0;
	  	  dp[0][1] = INF; 
		  for(int v : G[u])	
		  if(v != fa)
		  {
		  	 t++;
		  	 dp[t][0] = dp[t-1][0] + min(min(f[v][0][i][1],f[v][0][i][0]),min(f[v][1][i][1],f[v][1][i][0]));
		  	  dp[t][1] = min(dp[t-1][1] + min(min(f[v][2][i][1],f[v][2][i][0]),min(min(f[v][0][i][1],f[v][0][i][0]),min(f[v][1][i][1],f[v][1][i][0]))),dp[t-1][0] + min(f[v][2][i][1],f[v][2][i][0]));
		  }
		  if(k == 1) f[u][i][j][k] += dp[t][1];
		  else 
		  if(!(i+k) && j < 2)
		  {
		  	 for(int v : G[u]) 
		      if(v != fa)
		       f[u][i][j][k] += min(min(f[v][1][i][0],f[v][1][i][1]),f[v][0][i][1]);	
		  }
		  else f[u][i][j][k] += dp[t][0];
	  }
}
int main()
{
	while(~scanf("%d%d%d",&n,&c1,&c2) && (n+c1+c2))
	{
		for(int i = 1;i <= n;i++) G[i].clear();
		for(int i = 1;i < n;i++)
		{
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		dfs(1,0);
		cout<<min(min(min(f[1][0][0][1],f[1][0][0][0]),min(f[1][1][0][1],f[1][1][0][0])),min(f[1][2][0][1],f[1][2][0][0]))<<endl;
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值