POJ 3345 Bribing FIPA 树形dp

题意:

从一棵有n个节点的树中,每个节点均有一个权值。

当你选择了一个节点,则以该节点为根的所有的节点都属于你的。

找出m个点使得所花费的费用最少。

思路:

这题主要麻烦在字符串处理= =。不过也好,对atoi函数,substr函数又熟悉了。

由于没有根结点,因此要自己建立一个根节点。

状态定义:

dp[i][j]:以i节点为根的树中,获得j个节点所需要的最小费用。

转移方程:

dp[i][j] = min{dp[i][p] + dp[son][j-p] };

code:(写的有点挫)

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;

typedef long long LL;
const int MAXN = 205;
const int INF = 0x3f3f3f3f;

int n, m;
int dp[MAXN][MAXN];
int son[MAXN];
map <string, int> mp;
vector <int> G[MAXN];
int val[MAXN];
bool vis[MAXN];

int getSonNum(int u, int par)
{
	int num = 1;
	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(v == par) continue;
		num += getSonNum(v, u);
	}
	return son[u] = num;
}
		
void dfs(int u, int par)
{
	dp[u][0] = 0;
	//cout<<"u = "<<u<<" val = "<<val[u]<<endl;
	for(int i = 1;i <= son[u]; i++)
		dp[u][i] = val[u];

	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(v == par) continue;
		dfs(v, u);
		for(int t = min(m, son[u]); t >= 0; t--)
		{
			int tmp = dp[u][t];
			for(int p = 0;p <= t; p++)
				tmp = min(tmp, dp[u][p] + dp[v][t-p]);
			dp[u][t] = tmp;
		}
	}
}

void solve()
{
	memset(dp, INF, sizeof(dp));
	{
		int sum = 0;
		for(int i = 0;i < G[1].size(); i++)
		{
			int v = G[1][i];
			sum += val[v];
		}
		val[1] = sum;
	}
	getSonNum(1, -1);
	/**/
	son[1]--;
	dfs(1, -1);
	//cout<<"dp[3][3] = "<<dp[3][3]<<endl;
	cout<<dp[1][m]<<endl;
}

void init()
{
	memset(dp, INF, sizeof(dp));
	memset(vis, false, sizeof(vis));
	mp.clear();
	for(int i = 1;i <= n+1; i++)
		G[i].clear();
}
int main()
{
	ios::sync_with_stdio(false);
	string nm;
	//freopen("in.txt", "r", stdin);
	while(getline(cin, nm))
	{
		if(nm == "#") break;
		init();

		int mid;
		for(int i = 0;i < nm.length(); i++)
			if(nm[i] == ' ')
			{
				mid = i;
				break;
			}
		n = atoi(nm.substr(0, mid).c_str());
		m = atoi(nm.substr(mid+1).c_str());
		//cout<<"n = "<<n<<" m = "<<m<<endl;
		
		int cot = 2;		//the value record the number of node;
		for(int i = 0;i < n; i++)
		{
			getline(cin, nm);
			int last = 0, mid;
			bool token = false;
			int u;
			for(int j = 0;j < nm.length(); j++)
			{
				if(nm[j] != ' ' && j != nm.length()-1) continue;

				mid = j;
				if(j == nm.length() - 1) 
					mid++;

				if(nm[last] >= '0' && nm[last] <= '9') 
					val[u] = atoi(nm.substr(last, mid-last).c_str());
				else 
				{
					string tmp = nm.substr(last, mid - last);
					//cout<<"str = "<<tmp<<endl;
					//find tmp, whether is exist
					int rank;
					if(mp.find(tmp) != mp.end())
						rank = mp[tmp];
					else
					{
						rank = cot;
						mp[tmp] = cot++;
					}

					//build graph;
					if(token == false)
					{
						u = rank;
						token = true;
					}
					else
					{
						G[u].push_back(rank);
						vis[rank] = true; // while true, means node 1 shouldn't connect it;
					}
				}
				last = mid+1;
			}
		}
		//connect by 1;
		for(int i = 2;i <= n+1; i++)
		{
			if(vis[i]) continue;
			G[1].push_back(i);
		}
		/*
		cout<<"test"<<endl;
		for(int i = 1;i <= n+1; i++)
		{
			cout<<"u = "<<i<<endl;
			for(int j = 0;j < G[i].size(); j++)
			{
				cout<<G[i][j]<<" ";
			}
			cout<<endl;
		}
		*/

		solve();
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值