【带有估值函数的DP】 uva 10859 Placing Lampposts

题目大意:给一颗无向图树,每个节点都可以放灯,灯能点亮相邻的边,求怎么样才能把所有边都点亮。

如果有多种方案:给出一个能够让有两盏灯同时找到路情况最多的方案。

 

解析:

如果去掉后面的条件,就成为了一个很简单的树上dp。如何处理同时两盏灯找到的情况呢?

通常情况下:我们会将无根树变为有根树,建立一个dp[i][j] ,表示遍历为所有i及其子节点后,情况为j(0/1,放/不放)时的方案总数。 我们对转移函数进行修改:dp[i][j] = Cx + y,其中C为一个很大的数,使得无论取何值,y都发撼动x占主导地位的情况。

在这里,我们设置C = 2000, x表示放置灯的个数,y表示仅有一盏灯时照到的个数。

答案相应的变成了 ans/C, m-ans%C, ans%C,一个方程,全部解决。

 

#include 
using namespace std;
const int maxn = 1010;
const int M = 2000;
vector adj[maxn];
int vis[maxn][2],d[maxn][2],n,m;
int dp(int i, int j, int f)
{
	if(vis[i][j]) return d[i][j];
	vis[i][j] = 1;
	int& ans = d[i][j];
	
	ans += M;
	
	for (int k = 0 ; k < adj[i].size(); ++k)
	{
		int t = adj[i][k];
		if(t != f)
			ans += dp(t,1,i);
	}
	if(!j && f >= 0) ans++;
	
	if(j || f < 0)
	{
		int sum = 0;
		for (int k = 0 ; k < adj[i].size() ; ++k)
		{
			if(adj[i][k] != f)
				sum += dp(adj[i][k],0,i);
		}
		if(f >= 0)
			sum++;
		ans = min(ans,sum);
	}
	return ans;

}

int main()
{
	int T,a,b;
	cin >> T;
	while(T--)
	{
		cin >> n >> m;
		for (int i = 0 ; i < n ; ++i) adj[i].clear();
		memset(vis,0,sizeof(vis));
		memset(d,0,sizeof(d));
		for (int i = 0 ; i < m ; ++i)
		{
			cin >> a >> b;
			adj[a].push_back(b);
			adj[b].push_back(a);
		}

		int ans = 0;
		for (int i = 0 ; i < n ; ++i)
			if(!vis[i][0])
				ans += dp(i,0,-1);
		cout << ans/M << ' ' << m - ans%M << ' '<< ans%M << endl;
	}
    return 0;
}

 

 

 

你可能感兴趣的:(【动态规划】)