这题不难,输入较繁,典型的树形dp
题目大意:一共有n(<=200)个国家, 你想要用收买m(<=m)个国家,其中收买国家i的代价是v[ i ]。但是有些国家有拥属关系,如果A拥属B国,就是买通了A也意味着买通了B,而且这些关系是传递的。求最少需要付出的代价。
在树上背包,代码:
for (k=0;k<g[x].size();k++)
{
y=g[x][k];
for (i=num[x];i>=0;i--)
for (j=0;j<=i && j<=num[y];j++)
if (dp[x][i-j]+dp[y][j]<dp[x][i])
dp[x][i]=dp[x][i-j]+dp[y][j];
}
x为根,y是儿子,dp[x][i]表示以x为根的子树取i个的最小代价。(最好先把儿子都遍历完后再dp,这样比较清楚)
挺典型的,可以强化记忆。
这题的输入倒是有点e。。。
我用了sscanf,和sstream来搞。(表示c++是比pascal高级的多啊。。。)
还开了map。(第一次用map,挺方便的,但听说这东西巨慢。。。)
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
using namespace std;
const int maxn=205,oo=99999999;
vector<int> g[maxn];
int dp[maxn][maxn],v[maxn],num[maxn];
bool fa[maxn];
int n,m,id;
int dfs(int x)
{
num[x]=1;
int i,j,k,y;
for (k=0;k<g[x].size();k++)
{
y=g[x][k];
num[x]+=dfs(y);
}
for (k=0;k<=n;k++) dp[x][k]=oo;
dp[x][0]=0;
dp[x][num[x]]=v[x];
for (k=0;k<g[x].size();k++)
{
y=g[x][k];
for (i=num[x];i>=0;i--)
for (j=0;j<=i && j<=num[y];j++)
if (dp[x][i-j]+dp[y][j]<dp[x][i])
dp[x][i]=dp[x][i-j]+dp[y][j];
}
return num[x];
}
int main()
{
freopen("pin.txt","r",stdin);
freopen("pou.txt","w",stdout);
char str[1000];
int i,j,ans,now;
while (gets(str))
{
if (str[0]=='#') break;
sscanf(str,"%d%d",&n,&m);
map<string,int> wmap;
for (i=0;i<=n;i++)
g[i].clear();
memset(fa,0,sizeof(fa));
id=0;
for (i=1;i<=n;i++)
{
scanf("%s",str);
if (wmap.find(str)==wmap.end())
wmap[str]=++id;
now=wmap[str];
scanf("%d",&v[now]);
while (getchar()!='\n')
{
scanf("%s",str);
if (wmap.find(str)==wmap.end())
wmap[str]=++id;
g[now].push_back(wmap[str]);
fa[wmap[str]]=true;
}
}
v[0]=oo;
for (i=1;i<=n;i++)
{
if (fa[i]) continue;
g[0].push_back(i);
}
dfs(0);
ans=oo;
for (i=m;i<=n;i++)
if (dp[0][i]<ans) ans=dp[0][i];
printf("%d\n",ans);
}
return 0;
}