POJ3345 - 树型DP....要细心啊....T_T..

这题卡了将近一天....感觉好没状态啊...开始的DP状态转移还搞错了...搞得自己头都大了...中午看了集《全开Girl》..眼泪都要哗哗了....再来敲...居然给过了....好吧...

题目的意思就是有N个城市...我需要M个城市支持..每个城市都有支持所需要的费用....有些城市有隶属关系...那只要将老大的钱给了..隶属的城市就自动过来了....题目中有说每个城市最多隶属一个城市...没有形成环的情况...所以这就是一棵树...呃...不对...只是一片森林...因为可能有N个根节点....但我们可以把森林上端加一个超级根..连向所有跟...这个森林就成了一棵树..建图应该不要太过考虑...差不多是赤果果的...

输入很恶心.....只能一次读一行再来判断...要转整型..要转string的...手动来...而且一个点的孩子有多少个也不给出...也要手动来计数下...我的input就写了无比的长...eegache..

主要问题是如何来DP... 感觉这个DP有一点背包的思想...dp [ h ] [ k ] 代表第 h 号点这课子树有 k 个城市拉拢了的最少代价... 那么...

dp [ h ] [ i+ a[p] ] = Min (dp [ h ] [ i ]+ dp [ p ] [ a[p] ] ) < p是其所有孩子节点..a[p]代表p这课子树的节点数 >

大致思想就是这样...写的时候要多多注意细节..譬如...每次更新dp[ h ] 时不要直接更新...这样会造成重复更新....可以先用一个数组temp来记录dp[ h ]的更新情况...更新完后再将temp的值赋给dp [ h ]...


Program:

#include<iostream> #include<map> #include<string.h> #include<stdio.h> #include<queue> using namespace std; map<string,int> mymap; int n,m,a[205],father[205],totol[205],son[205],temp[205]; char s[1000]; queue<int> myqueue; void input() { int x,p,l,k,Num,g,i; string str; i=0; n=0; while (s[i]!=' ') n=n*10+s[i++]-'0'; i++; m=0; while (s[i]>='0' && s[i]<='9' ) m=m*10+s[i++]-'0'; memset(father,0,sizeof(father)); mymap.clear(); p=0; for (i=1;i<=n;i++) { gets(s); l=strlen(s); str=""; k=0; while (s[k]!=' ') str+=s[k++]; k++; x=mymap[str]; if (!x) { mymap[str]=++p; x=p; } a[x]=0; while (s[k]>='0' && s[k]<='9' ) a[x]=a[x]*10+s[k++]-'0'; k++; Num=0; while (k<l) { str=""; Num++; while (s[k]!=' ' && s[k]!='\n' && k<l) str+=s[k++]; g=mymap[str]; if (!g) { mymap[str]=++p; g=p; } father[g]=x; k++; } son[x]=Num; } son[0]=0; for (i=1;i<=n;i++) if (!father[i]) son[0]++; } int GetAnswer() { int dp[205][205],i,j,k,h,f; memset(dp,-1,sizeof(dp)); for (i=0;i<=n;i++) dp[i][0]=0; while (!myqueue.empty()) myqueue.pop(); for (i=1;i<=n;i++) { if (!son[i]) myqueue.push(i); totol[i]=1; } totol[0]=0; while (1) { h=myqueue.front(); myqueue.pop(); if (!h) break; dp[h][totol[h]]=a[h]; son[father[h]]--; if (!son[father[h]]) myqueue.push(father[h]); f=father[h]; totol[f]+=totol[h]; for (i=0;i<=n;i++) temp[i]=dp[f][i]; for (k=1;k<=totol[h];k++) for (i=totol[f]-k;i>=0;i--) if (dp[f][i]>=0) { if (temp[i+k]<0 || temp[i+k]>dp[f][i]+dp[h][k]) temp[i+k]=dp[f][i]+dp[h][k]; } for (i=0;i<=n;i++) dp[f][i]=temp[i]; } int ans=2000000000; for (k=0;k<=n;k++) for (i=m;i<=n;i++) if (dp[k][i]>=0) ans=ans<dp[k][i]?ans:dp[k][i]; return ans; } int main() { while (gets(s)) { if (s[0]=='#') break; input(); printf("%d\n",GetAnswer()); } return 0; }

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值