有線電視網(樹形dp)
阿新 • • 發佈:2018-02-07
code 一個 傳輸 mes 收益 滿足 -m 收費 -- 為,對於第i個點,用前k個兒子,滿足j個客戶的最大收益。於是狀態轉移方程就是:\(dp[k][i][j]=max(dp[k-1][i][p]+dp[k][i][j-p]\ (0\le p\le j)\)。觀察一下這個狀態和方程,它是一個多決策最優化問題,滿足最優化原理,且無後效性。同時k是可以被滾動數組壓縮的,從後往前循環枚舉p即可。
有線電視網
某收費有線電視網計劃轉播一場重要的足球比賽。他們的轉播網和用戶終端構成一棵樹狀結構,這棵樹的根結點位於足球比賽的現場,樹葉為各個用戶終端,其他中轉站為該樹的內部節點。從轉播站到轉播站以及從轉播站到所有用戶終端的信號傳輸費用都是已知的,一場轉播的總費用等於傳輸信號的費用總和。現在每個用戶都準備了一筆費用想觀看這場精彩的足球比賽,有線電視網有權決定給哪些用戶提供信號而不給哪些用戶提供信號。寫一個程序找出一個方案使得有線電視網在不虧本的情況下使觀看轉播的用戶盡可能多。2≤N≤3000。
關於樹形dp,有一個經典套路,就是n^3動歸,從下到上枚舉每個點,每個點下的孩子和一個值。這道題也不例外。設計狀態\(dp[k][i][j]\)
#include <cstdio>
using namespace std;
const int maxn=3005, maxm=3005, INF=1e9;
struct Graph{
struct Edge{
int to, next, v; Graph *bel;
inline int operator *(){ return to; }
Edge& operator ++(){
return *this=bel->edge[next]; }
};
void addedge(int x, int y, int v){
Edge &e=edge[++cntedge];
e.to=y; e.next=fir[x]; e.v=v;
e.bel=this; fir[x]=cntedge;
}
Edge& getlink(int x){
return edge[fir[x]]; }
Edge edge[maxm*2];
int cntedge, fir[maxn];
}g;
int n, m, size[maxn];
int dp[maxn][maxn];
inline int max(int x, int y){
return x<y?y:x; }
void predfs(int now, int par){
Graph::Edge e=g.getlink(now);
for (; *e; ++e){
if (*e==par) continue;
predfs(*e, now);
size[now]+=size[*e];
}
if (size[now]==0) size[now]=1;
}
void dfs(int now, int par){
Graph::Edge e=g.getlink(now);
dp[now][0]=0; int cntsize=0;
for (; *e; ++e){ //枚舉孩子
if (*e==par) continue; dfs(*e, now);
cntsize+=size[*e];
for (int j=cntsize; j>=0; --j) //總客戶數
for (int p=cntsize-size[*e]; p>=0; --p) //前面的孩子中有幾個客戶
dp[now][j]=max(dp[now][j],
dp[now][p]+dp[*e][j-p]-((j-p)?e.v:0));
}
}
void init(){
for (int i=0; i<maxn; ++i)
for (int j=0; j<maxn; ++j)
dp[i][j]=-INF;
}
int main(){
init();
scanf("%d%d", &n, &m); int k, a, c;
for (int i=1; i<=n-m; ++i){
scanf("%d", &k);
for (int j=1; j<=k; ++j){
scanf("%d%d", &a, &c);
g.addedge(i, a, c);
}
}
for (int i=n-m+1; i<=n; ++i) scanf("%d", &dp[i][1]);
predfs(1, 0); dfs(1, 0); int pos;
for (pos=size[1]; pos>=0; --pos)
if (dp[1][pos]>=0) break;
printf("%d", pos); return 0;
}
有線電視網(樹形dp)