題目連結:http://poj.org/problem?id=1155
題意:給定一棵樹,1為根結點表示電視臺,有m個葉子節點表示客戶,有n-m-1箇中間節點表示中轉站,每條樹邊有權值。現在要在電視臺播放一場比賽,每個客戶願意花費cost[i]的錢觀看,而從電視臺到每個客戶也都有個費用,並且經過一條邊只會產生一個費用。問電視臺不虧損的情況最多有幾個客戶可以看到比賽?
思路:在樹上的揹包,具體看程式碼註釋。
程式碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=3e3+;
const int INF=0x3f3f3f3f; struct Edge
{
int v;
int w;
int next;
} edge[maxn<<]; int dp[maxn][maxn];
int N,M;
int head[maxn];
int num[maxn],temp[maxn]; ///num陣列是記錄以當前節點為根節點的子樹最多可以連多少個使用者
int I; ///temp陣列是為了維護當前狀態,防止更新過程狀態的變化影響結果 void init()
{
I=;
memset(head,-,sizeof(head));
} void addedge(int x,int y,int w)
{
edge[I].v=y;
edge[I].w=w;
edge[I].next=head[x];
head[x]=I++;
} void dfs(int u) /// 如果dp[0-N][0]=-INF 結果是WA的
{
///這個就是保證跟使用者相連的時候第一個就算是負發也要選進來,其他的正數可以彌補。
if(u>N-M) return; ///否則求得的不是最優解
for(int i=head[u]; i!=-; i=edge[i].next)
{
int v=edge[i].v;
dfs(v);
for(int k=; k<=num[u]; k++)
temp[k]=dp[u][k];
for(int j=; j<=num[u]; j++)
for(int k=; k<=num[v]; k++)
dp[u][j+k]=max(dp[u][j+k],temp[j]+dp[v][k]-edge[i].w);
num[u]+=num[v];
}
} int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&N,&M)==)
{
init();
for(int i=; i<=N; i++) for(int j=; j<=N; j++) dp[i][j]=-INF;///初始化時候一定要注意!
memset(num,,sizeof(num));
for(int i=; i<=N-M; i++)
{
int n;
scanf("%d",&n);
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
addedge(i,a,b);
}
}
for(int i=N-M+; i<=N; i++)
{
scanf("%d",&dp[i][]);
num[i]=;
}
dfs();
for(int i=M; i>=; i--)
{
if(dp[][i]>=)
{
printf("%d\n",i);
break;
}
}
}
return ;
}