poj 1155 tele
阿新 • • 發佈:2019-01-06
題目大意:
這道題的大意就是有N個節點,1號節點為發射站,2-n-m 是中途的傳遞站,n-m+1到n為使用者,使用者收到電視訊號就會付出一定的錢,其中每條邊有邊權,代表傳遞的花費,
問在不虧本的情況下最多能給多少個使用者傳送訊號。
題目分析:
這道題就是在樹上揹包,用Dp[i][j]表示以i為根節點管理j個使用者所得到的最大收益。
接下來處理細節,怎麼知道管理的使用者的個數,用一個num陣列標記,使用者設為1,傳遞站設為0,在dfs求解子樹的過程中進行更新,num[root]=∑num[son];
關於使用者付出的金錢,直接用dp[i][0]表示,方便dp的時候使用;
那麼dp方程就是:
dp[root][j+k]=max(dp[root][j+k],dp[root][j]+dp[son][k]);這就是一個揹包;
我們採用正推的方式更新。
但是我們需要思考一個問題,在dp更新的時候可能會出現一些錯誤,例如在更新的過程中dp[root][j]可能是已經使用過這棵son子樹了,而我們的j是在除當前son子樹以外的使用者數,所以我們需要用一個臨時陣列tem提取出來。
最後輸出最大的i使得dp[1][i]大於0;
如下程式碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 3100 #define INF 0x7fffffff using namespace std; int n,m; int dp[N][N],head[N],a,b,num[N],len,k,tem[N]; struct Node { int now,next,w; }edge[N*2]; void dfs(int root,int fa) { int p; for(int i=head[root];i!=-1;i=edge[i].next) { p=edge[i].now; if(p==fa) continue; dfs(p,root); for(int j=0;j<=num[root];j++) tem[j]=dp[root][j]; for(int j=0;j<=num[root];j++) for(int k=1;k<=num[p];k++) { dp[root][j+k]=max(dp[root][j+k],tem[j]+dp[p][k]-edge[i].w); } num[root]+=num[p]; } } void addedge(int u,int v,int W) { edge[len].now=v; edge[len].next=head[u]; edge[len].w=W; head[u]=len++; } int main() { scanf("%d%d",&n,&m); len=0; memset(head,-1,sizeof(head)); for(int i=1;i<=n-m;i++) { num[i]=0; scanf("%d",&k); for(int j=1;j<=k;j++) { scanf("%d%d",&a,&b); addedge(i,a,b); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dp[i][j]=-INF; for(int i=n-m+1;i<=n;i++) { num[i]=1; scanf("%d",&dp[i][1]); } dfs(1,-1); for(int i=m;i>=0;i--) { if(dp[1][i]>=0) { printf("%d\n",i); break; } } //while(1); return 0; }