树形DP,不同的是,只要选择一个根节点i,它的子树中的所有节点也被选择,代价为cost[i]
由于dfs时,先遍历子树,再返回根节点,这样,以i为根的子树中的节点可能被多次利用,解决办法就是访问到i时,先开一个tmp数组当前的状态,即dp值,然后访问子树,返回时,先更新tmp值,再用tmp值更新dp值
还有一个trick就是,题目要求的是至少m张选票,不是刚好m张,所以可以大于m
代码:
- #include<iostream>
- #include<memory.h>
- #include<string>
- #include<cstdio>
- #include<algorithm>
- #include<math.h>
- #include<stack>
- #include<queue>
- #include<vector>
- #include<map>
- using namespace std;
- struct node
- {
- int v,next;
- }g[205*205];
- map<string,int>mp;
- int adj[205],dp[405],cnt[205],e,in[205],cost[205];
- int n,m;
- char s[105];
- void add(int u,int v)
- {
- g[e].v=v; g[e].next=adj[u]; adj[u]=e++;
- }
- void dfs(int u)
- {
- int i,k,v;
- cnt[u]=1;
- int tmp[405];
- memcpy(tmp,dp,sizeof(dp));
- for(i=adj[u];i!=-1;i=g[i].next)
- {
- v=g[i].v;
- dfs(v);
- cnt[u]+=cnt[v];
- }
- for(i=m+cnt[u];i>=cnt[u];i--)
- {
- //k=min(i,m);
- tmp[i]=min(tmp[i],tmp[i-cnt[u]]+cost[u]);
- dp[i]=min(dp[i],tmp[i]);
- }
- }
- int main()
- {
- int i,j,k,l,w,idx;
- char c;
- while(gets(s))
- {
- if(s[0]=='#')
- break;
- sscanf(s,"%d%d",&n,&m);
- mp.clear();
- memset(adj,-1,sizeof(adj));
- memset(in,0,sizeof(in));
- e=idx=0;
- for(i=1;i<=n;i++)
- {
- dp[i]=dp[i+n]=1<<20;
- scanf("%s%d",s,&w);
- k=mp[s];
- if(k==0)
- {
- mp[s]=++idx;
- k=idx;
- }
- cost[k]=w;
- c=getchar();
- while(c==' ')
- {
- scanf("%s",s);
- l=mp[s];
- if(l==0)
- {
- mp[s]=++idx;
- l=idx;
- }
- add(k,l);
- in[l]++;
- c=getchar();
- }
- }
- dp[0]=0;
- for(i=1;i<=n;i++)
- {
- if(in[i]==0)
- dfs(i);
- }
- int ans=1<<20;
- for(i=m;i<=2*n;i++)
- ans=min(dp[i],ans);
- printf("%d\n",ans);
- }
- return 0;
- }