1. 程式人生 > >[BZOJ1722] [Usaco2006 Mar] Milk Team Select產奶比賽 解題報告

[BZOJ1722] [Usaco2006 Mar] Milk Team Select產奶比賽 解題報告

i++ http 理解 範圍 zoj 維數 class 一道 ()

首先,要明確這是一道動態規劃題(看著很像背包嘛)那我們要用什麽作為下表呢?

  1. 以牛奶量:牛奶量的數據範圍較大,最多只能開一維數組(除非你開滾動數組)。總體來說較難考慮。
  2. 以關系數:關系數最多只有 N - 1 對,可以給每頭牛開一個 MAXN 的數組,看起來更行得通。。。

還有,因為要考慮關系數的轉換問題,所以還有開一維[0/1]表示這頭牛取不取。

我們發現,這些關系是無環的,或者說這是一個森林(本來我想用拓撲排序,後來還是放棄了。。。),所以可以再建立一頭“牛祖先”,連接原來所有祖先,構成一棵樹(即無母親的節點都認0為母親)。

對於樣例,可以構成如圖的一棵樹:

技術分享圖片

整理一下,剛才講到建立一個 int f[MAXN][MAXN][2]; 的數組,f[i]j[j][op] 表示以第 i 個節點為根的樹 共有 j 對關系,op = 1 時表示取這個節點,op = 0 時表示不取。

可以把 關系數 作為背包容量,牛奶量 作為 價值

用分組背包的方法來考慮。

具體的部分自己慢慢理解吧。

上代碼——

#include<cstdio>
#include<iostream>
using namespace std;
#define MAXN 505
#define INF 0x3f3f3f3f

int
N, X, ans; int c[MAXN], t; int head[MAXN], to[MAXN << 1], nxt[MAXN << 1], tot(0); int f[MAXN][MAXN][2]; void Add( int x, int y ){ to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; } void DFS( int x, int fa ){ for ( int i = 1; i <= N; i++ ) f[x][i][0] = f[x][i][1] = -INF;//初始值均為負無窮
for ( int i = head[x]; i; i = nxt[i] ){ if ( to[i] == fa ) continue; DFS( to[i], x );//先完成子節點 for ( int j = N; j >= 0; --j ){//分配的關系數 for ( int k = 0; k <= j; ++k ){//背包—— f[x][j][1] = max( f[x][j][1], f[x][j - k][1] + max( k == 0 ? -INF : f[to[i]][k - 1][1], f[to[i]][k][0] ) ); f[x][j][0] = max( f[x][j][0], f[x][j - k][0] + max( f[to[i]][k][1], f[to[i]][k][0] ) ); } } } for ( int i = 0; i <= N; ++i ) f[x][i][1] += c[x];//若取這個節點,加上這個點的價值 } int main(){ scanf( "%d%d", &N, &X ); for ( int i = 1; i <= N; ++i ){ scanf( "%d%d", &c[i], &t ); Add( i, t ); Add( t, i );//鏈式前向星存邊 } DFS( 0, -1 ); int ans(N); while( ans >= 0 ){ if ( f[0][ans][0] >= X ) break; ans--; } printf( "%d\n", ans ); return 0; }

[BZOJ1722] [Usaco2006 Mar] Milk Team Select產奶比賽 解題報告