题解 POJ3345 【Bribing FIPA】

题目链接:Link

Problem

Solution

令 $ f(u,i)= $ u为根节点的子树中,得i票的最小代价。
若有一个决策,代价为w,得v票,则 $ f(u,i)=min(f(u,i),f(u,i-v)+w) $
不难发现,递归求取子树的解后,可将每个子树视为一个泛化物品,用分组背包求解即可。

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#include<sstream>
#include<vector>
using namespace std;
const int maxn=205;
const int oo=0x3f3f3f3f;
map<string,int> mp;
int n,m,id,w[maxn],fa[maxn],sum[maxn],f[maxn][maxn];
inline int ID(const string &s)
{
	if(mp.find(s)==mp.end()) mp[s]=++id;
	return mp[s];
}
vector<int> G[maxn];
void dfs(int u)
{
	sum[u]=1;
	for(int i=0;i<G[u].size();i++) { dfs(G[u][i]); sum[u]+=sum[G[u][i]]; }
	f[u][0]=0;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		for(int j=sum[u];j>=0;j--)
			for(int k=0;k<=sum[v]&&k<=j;k++)
				f[u][j]=min(f[u][j],f[u][j-k]+f[v][k]);
	}
	f[u][sum[u]]=min(f[u][sum[u]],w[u]);
}
int main()
{
	#ifdef local
	freopen("pro.in","r",stdin);
	#endif
	w[0]=oo;
	while(cin>>n>>m)
	{
		id=0; mp.clear(); memset(fa,0,sizeof(fa)); memset(f,0x3f,sizeof(f)); memset(sum,0,sizeof(sum));
		for(int i=0;i<=n;i++) G[i].clear();
		string tmp; getline(cin,tmp);
		for(int i=1;i<=n;i++)
		{
			string s; getline(cin,s);
			stringstream ss(s);
			ss>>s;
			int u=ID(s);
			ss>>w[u];
			while(ss>>s) fa[ID(s)]=u;
		}
		for(int i=1;i<=n;i++) G[fa[i]].push_back(i);
		dfs(0);
		int res=oo;
		for(int i=m;i<=n;i++) res=min(res,f[0][i]);
		printf("%d\n",res);
	}
	return 0;
}
posted @ 2019-10-04 21:13  happyZYM  阅读(102)  评论(0编辑  收藏  举报