poj3345 树型DP

这题不难,输入较繁,典型的树形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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值