Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 1018 Accepted Submission(s): 376
题目大意:给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线。现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit。1<=n<=1000,1<=m<=100万
解题思路:一看到题目就觉得是树形DP,男人的第六感怎一个准字了得。然后努力地把题目看懂了以后,发现这是一个判定性问题,就是问某个limit是否能够满足条件切断所有前线与司令部的联系,然后找符合条件的最大limit就可以了。想通这点以后就可以二分答案,下限为1,上限为最大的边权,再写一个Tree_DP(limit),判断切断所有前线联系时花费的最小费用是否小等于m,是的话就往后查找,否则向前。
设dp[i]为切断i的所有子孙叶子所花费的最小费用,状态转移方程: if (i->son.len < limit) dp[i] += min(dp[i->son],i->son.len); (如果与子节点相连的边可选)
else dp[i] += dp[i->son];(如果与子节点相连的边不可选)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int VM=1010; const int INF=1000010; //开始设为0x3f3f3f3f给了WA struct Edge{ int to,nxt; int cap; }edge[VM<<1]; int n,m,cnt,head[VM]; int dp[VM],vis[VM]; void addedge(int cu,int cv,int cw){ edge[cnt].to=cv; edge[cnt].cap=cw; edge[cnt].nxt=head[cu]; head[cu]=cnt++; } void DFS(int u,int pre,int lim){ int flag=0; //标记是不是叶子结点 dp[u]=0; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v!=pre){ flag=1; DFS(v,u,lim); if(edge[i].cap<=lim) dp[u]+=min(dp[v],edge[i].cap); else dp[u]+=dp[v]; } } if(!flag) //叶子结点无穷大 dp[u]=INF; } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ if(n==0 && m==0) break; cnt=0; memset(head,-1,sizeof(head)); int MAX=0; int u,v,w; for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); if(w>MAX) MAX=w; } int l=1,r=MAX,ans=-1; while(l<=r){ int mid=(l+r)>>1; DFS(1,-1,mid); if(dp[1]<=m){ ans=mid; r=mid-1; }else l=mid+1; } printf("%d\n",ans); } return 0; }