一,題意
給定 n 個敵方據點,n 個點相連構成一棵樹,1 爲司令部,樹中每條邊都有一個權值 cost 表示破壞這條邊的費用。
葉子節點爲前線。現要切斷前線和司令部的聯繫,要求你截斷所有葉子節點的信息通過切斷樹中的一些邊。
每次切斷邊的費用不能超過上限 limit,切斷所有前線與司令部聯繫所花費的總費用要少於 m 要求最小的 limit。
二,解析:
該題主要應用了,樹狀DP的建樹與思想 + 二分查找。
1,數組dp:樹狀dp最重要的就兩點,一是:建樹,二是:遞推。
對於建樹,其實建立一個單項鍊表,該單項鍊表頭節點就是圖的節點,起後面連接的就是該節點所鄰接的邊(如圖)。
對於一般的數位dp思想是利用子節點的與父節點的關係來確定遞推關係。
2,該題能用到二分查找主要因爲兩點:一是收索的區間[low,tall]確定,二是因爲要求的是該區間的最值。
三,解析:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const int Inf=1000010;
struct node
{//邊結構體
int to,next,weight;
};
node Edge[2000];
int Size;
int head[1000];//節點數組
int N,M,total;
int mid;
void add(int from,int to,int weight)
{//向圖中插入一條邊
//這個建圖過程相當於單向鏈表的頭插法,head數組是各鏈表的頭節點
Edge[Size].to=to;
Edge[Size].weight=weight;
Edge[Size].next=head[from];
head[from]=Size++;
}
int DFS(int dan,int father)
{//由於每次邊的限制都不同所以DFS的路徑也不同所以不需要記錄狀態
//即使記錄了狀態也在後面也沒有用到。
int res=0;//葉子節點邊界
bool flog=false;
for(int x=head[dan];x!=-1;x=Edge[x].next)
{
int to=Edge[x].to;
if(to==father)
continue;
int total=DFS(to,dan);
if(total>Edge[x].weight && Edge[x].weight<=mid)
total=Edge[x].weight;
res+=total;
flog=true;
}
if(flog)
return res;
else
return Inf;
}
int main()
{
while(scanf("%d%d",&N,&M)!=EOF&&M)
{
int x,y,w;
Size=0;
int low=Inf,tall=-1;
memset(head,-1,sizeof(head));
for(int i=1;i<N;i++)
{
scanf("%d%d%d",&x,&y,&w);
if(low>w)
low=w;
if(tall<w)
tall=w;
add(x,y,w);
add(y,x,w);
}
int key=Inf;
while(low<=tall)
{//二分查找
mid=(low+tall)/2;
if(DFS(1,0)<=M)
{
key=mid;
tall=mid-1;
}
else
low=mid+1;
}
if(key!=Inf)
cout<<key<<endl;
else
cout<<"-1"<<endl;
}
return 0;
}