1. 程式人生 > >洛谷P1273 有線電視網 (樹上分組背包)

洛谷P1273 有線電視網 (樹上分組背包)

pac p12 足球 邊界 i+1 輸入輸出格式 行數據 cnblogs 應該

洛谷P1273 有線電視網

題目描述

某收費有線電視網計劃轉播一場重要的足球比賽。他們的轉播網和用戶終端構成一棵樹狀結構,這棵樹的根結點位於足球比賽的現場,樹葉為各個用戶終端,其他中轉站為該樹的內部節點。

從轉播站到轉播站以及從轉播站到所有用戶終端的信號傳輸費用都是已知的,一場轉播的總費用等於傳輸信號的費用總和。

現在每個用戶都準備了一筆費用想觀看這場精彩的足球比賽,有線電視網有權決定給哪些用戶提供信號而不給哪些用戶提供信號。

寫一個程序找出一個方案使得有線電視網在不虧本的情況下使觀看轉播的用戶盡可能多。

輸入輸出格式

輸入格式:

輸入文件的第一行包含兩個用空格隔開的整數N和M,其中2≤N≤3000,1≤M≤N-1,N為整個有線電視網的結點總數,M為用戶終端的數量。

第一個轉播站即樹的根結點編號為1,其他的轉播站編號為2到N-M,用戶終端編號為N-M+1到N。

接下來的N-M行每行表示—個轉播站的數據,第i+1行表示第i個轉播站的數據,其格式如下:

K A1 C1 A2 C2 … Ak Ck

K表示該轉播站下接K個結點(轉播站或用戶),每個結點對應一對整數A與C,A表示結點編號,C表示從當前轉播站傳輸信號到結點A的費用。最後一行依次表示所有用戶為觀看比賽而準備支付的錢數。

輸出格式:

輸出文件僅一行,包含一個整數,表示上述問題所要求的最大用戶數。

輸入輸出樣例

輸入樣例#1:

5 3
2 2 2 5 3
2 3 2 4 3
3 4 2

輸出樣例#1:

2

說明

樣例解釋

技術分享圖片

如圖所示,共有五個結點。結點①為根結點,即現場直播站,②為一個中轉站,③④⑤為用戶端,共M個,編號從N-M+1到N,他們為觀看比賽分別準備的錢數為3、4、2,從結點①可以傳送信號到結點②,費用為2,也可以傳送信號到結點⑤,費用為3(第二行數據所示),從結點②可以傳輸信號到結點③,費用為2。也可傳輸信號到結點④,費用為3(第三行數據所示),如果要讓所有用戶(③④⑤)都能看上比賽,則信號傳輸的總費用為:

2+3+2+3=10,大於用戶願意支付的總費用3+4+2=9,有線電視網就虧本了,而只讓③④兩個用戶看比賽就不虧本了。

Solution

首先,樹形結構,應該可以看出來吧
其次,題目中給了我們一個條件,就是收取的費用一定要大於等於建造的費用

所以可以看做是一個背包
樹上背包!!!
具體是什麽背包呢?

我們把每個節點所選的用戶數看成一個個元素,比如選一個用戶是一個元素,選兩個用戶也是一個元素,其中這些元素是互斥的,熟悉背包的同學應該已經看出來了

分組背包:有若幹組物品,其中每組物品都只能選一個

那麽在這道題中,容量就是以一個節點為根的子樹的節點數,組數就是子節點的個數,我們要做的就是枚舉每一組中的元素選擇多少個客戶

\(dp[i][j]\)為以i為根的子樹中選擇j個客戶的花費
目標狀態:\(max(i),dp[1][i]>=0\)

怎麽轉移?
\[dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-w)\]
解釋一下,其中v是u的子節點,w是\(u\to v\)的花費,j和k都是枚舉的選擇客戶的個數,但j的範圍是u的整個子樹的節點數,k是子節點的子樹的節點數

邊界

memset(dp,~0x3f,sizeof(dp));
for(rg int i=1;i<=n;i++) dp[i][0]=0;//每個節點都不選,花費當然是0

Code

#include<bits/stdc++.h>
#define in(i) (i=read())
#define rg register
#define il extern inline
using namespace std;

const int N=3e3+10;

int read() {
    int ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=ans*10+(i^48),i=getchar();
    return ans*f;
}

int n,m,cur;
int to[N],nex[N],head[N],w[N];
int dp[N][N],v[N];
il void add(int a,int b,int c) {
    to[++cur]=b,nex[cur]=head[a];
    w[cur]=c,head[a]=cur;
}
int dfs(int u,int sum=0) {
    if(u>=n-m+1) {
        dp[u][1]=v[u];
        return 1;
    }
    for(rg int i=head[u];i;i=nex[i]) {
        int t=dfs(to[i]);sum+=t;
        for(rg int j=sum;j>=1;j--) {
            for(rg int k=1;k<=t;k++) {
                if(j-k>=0) dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[to[i]][k]-w[i]);
            }
        }
    }return sum;
}
int main()
{
    in(n),in(m); memset(dp,~0x3f,sizeof(dp));
    for(rg int i=1;i<=n-m;i++) {
        int k,a,b; in(k);
        for(rg int j=1;j<=k;j++)
            in(a),in(b),add(i,a,b);
    }
    for(rg int i=n-m+1;i<=n;i++) in(v[i]);
    for(rg int i=1;i<=n;i++) dp[i][0]=0;
    dfs(1);
    for(rg int i=m;i>=1;i--)
    if(dp[1][i]>=0) cout<<i<<endl,exit(0);
}

博主蒟蒻,隨意轉載.但必須附上原文鏈接

http://www.cnblogs.com/real-l/

洛谷P1273 有線電視網 (樹上分組背包)