Description
Farmer John的N(1<=N<=500)頭奶牛打算組隊去參加一個世界級的產奶比賽(Multistate Milking Match-up,縮寫爲MMM)。她們很清楚其他隊的實力,也就是說,她們派出的隊只要能產出至少X(1<=X<=1,000,000)加侖牛奶,就能贏得這場比賽。
每頭牛都能爲集體貢獻一定量的牛奶,數值在-10,000到10,000之間(有些奶牛總是想弄翻裝着其他奶牛產的奶的瓶子)。
MMM的舉辦目的之一,是通過競賽中的合作來增進家庭成員之間的默契。奶牛們認爲她們總是能贏得這場比賽,但爲了表示對比賽精神的支持,她們希望在選出的隊伍裏能有儘可能多的牛來自同一個家庭,也就是說,有儘可能多對的牛有直系血緣關係(當然,這支隊伍必須能產出至少X加侖牛奶)。當然了,所有的奶牛都是女性,所以隊伍裏所有直系血親都是母女關係。
FJ熟知所有奶牛之間的血緣關係。現在他想知道,如果在保證一支隊伍能贏得比賽的情況下,隊伍中最多能存在多少對血緣關係。注意,如果一支隊伍由某頭奶牛和她的母親、她的外祖母組成,那這支隊伍裏一共有2對血緣關係(這頭奶牛外祖母與她的母親,以及她與她的母親)。
Input
第1行: 兩個用空格隔開的整數 N 和 X
第2..N+1行: 每行包括兩個用空格隔開的整數,第i+1行的數描述了第i頭牛的情況:第一個數爲她能貢獻出的牛奶的加侖數,第二個數表示她的母親的編號(數值在1..N的範圍內)。如果一頭牛的母親不在整 個牛羣裏,那第二個數爲0。並且,血緣信息不會出現循環,也 就是說一頭奶牛不會是自己的母親或祖母,或者更高代的祖先
Output
* 第1行: 輸出在一個能獲勝的隊伍中,最多可能存在的有血緣關係的牛的對數 。如果任何一支隊伍都不可能獲勝,輸出-1
Sample Input
5 8
-1 0
3 1
5 1
-3 3
2 0
輸入說明:
FJ一共有5頭奶牛。第1頭奶牛能提供-1加侖的牛奶,且她是第2、第3頭奶牛的母親。第2、第3頭奶牛的產奶量分別爲3加侖和5加侖。第4頭奶牛是第3頭奶牛的女兒,她能提供-3加侖牛奶。還有與其他牛都沒有關係的第5頭奶牛,她的產奶量是2加侖。
Sample Output
2
輸出說明:
最好的一支隊伍包括第1,2,3,5頭奶牛。她們一共能產出(-1)+3+5+2=9>=8加侖牛奶,並且這支隊伍裏有2對牛有血緣關係(1–2 和 1–3)。如果只選第2,3,5頭奶牛,雖然總產奶量會更高(10加侖),但這支隊伍裏包含的血緣關係的對數比上一種組合少(隊伍裏沒有血緣關係對)。
這個題要注意細節和優化。
我最先推出的DP方程很複雜。
然後逐漸優化,和簡化,
去掉重複計算的狀態。
最後是這個樣子:
設
F[i][j][flag]=⎧⎩⎨以i爲根的子樹(包含i這個節點)有j對血緣關系數以i爲根的子樹(不包含包含i這個節點)有j對血緣關系數flag=1flag=0
然後DP方程爲:
F[v][j][flag]=⎧⎩⎨max(F[v][j][1],F[v][j−k][1]+F[p][k−1][1])max(F[v][j][1],F[v][j−k][1]+F[p][k][0])max(F[v][j][0],F[v][j−k][0]+max(F[p][k][0],F[p][k][1]))(flag=1,k≥1)(flag=1)(flag=0)
代碼如下:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
struct node {
int to;
int next;
int c;
}w[2050];
int n,x;
int cnt=1;
int h[601];
int F[602][600][2];
void AddEdge(int a,int y,int c){
cnt++;
w[cnt].to=y;
w[cnt].next=h[a];
w[cnt].c=c;
h[a]=cnt;
}
void Dfs(int v,int fa,int c){
F[v][0][1]=c;
F[v][0][0]=0;
for(int i=1;i<=n;i++){
F[v][i][1]=F[v][i][0]=-0x7fffffff/2;
}
for(int i=h[v];i;i=w[i].next){
int y=w[i].to;
if(y!=fa){
Dfs(y,v,w[i].c);
for(int j=n;j>=0;j--){
for(int k=0;k<=j;k++){
if(k>=1)F[v][j][1]=max(F[v][j][1],F[v][j-k][1]+F[y][k-1][1]);
F[v][j][1]=max(F[v][j][1],F[v][j-k][1]+F[y][k][0]);
F[v][j][0]=max(F[v][j][0],F[v][j-k][0]+max(F[y][k][0],F[y][k][1]));
}
}
}
}
}
int main(){
scanf("%d%d",&n,&x);
int a,b;
for(int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
AddEdge(i,b,a);
AddEdge(b,i,a);
}
Dfs(0,0,0);
int Ans=0;
int flag=0;
for(int i=0;i<=n;i++){
if(F[0][i][0]>=x)
Ans=i,flag=1;
}
if(flag)cout<<Ans;
else cout<<"-1";
return 0;
}
然後大家會發現這個程序的速度會比較慢,
因爲它的細節優化不夠完美。
給出一份速度快的標程:
//Source : Adrian Diaconu
#include <stdio.h>
#include <string.h>
#define NMAX 511
#define LMAX 2100
#define INF 5110111
int n, x, par[NMAX], val[NMAX], nr[NMAX], urm[NMAX], z, rez,
sol[NMAX][2][NMAX], v[NMAX], aux[2][NMAX];
void Vadd(int i,int j) {
urm[++z] = v[i];
v[i] = z;
nr[z] = j;
}
void parc(int i) {
int j, g, k, t, r=(i!=0);
for(j = v[i]; j; j = urm[j])
parc(nr[j]);
for(sol[i][0][0] = 0, sol[i][1][0] = val[i], j = 1; j <= n; j++)
sol[i][0][j] = sol[i][1][j] = -INF;
for(j=v[i]; j; j=urm[j]) {
memcpy(aux, sol[i], sizeof(sol[i]));
for(g=nr[j], k=0; k<=n; k++)
if(sol[g][1][k]>-INF||sol[g][0][k]>-INF)
for(t=0;t+k<=n;++t) {
if (aux[0][t] > -INF && aux[0][t]+sol[g][0][k] > sol[i][0][t+k])
sol[i][0][t+k] = aux[0][t] + sol[g][0][k];
if (aux[0][t] > -INF && aux[0][t]+sol[g][1][k] > sol[i][0][t+k])
sol[i][0][t+k] = aux[0][t]+sol[g][1][k];
if(aux[1][t] > -INF && aux[1][t]+sol[g][0][k] > sol[i][1][t+k])
sol[i][1][t+k] = aux[1][t]+sol[g][0][k];
if(t+k+r<=n)
if(aux[1][t] > -INF && aux[1][t]+sol[g][1][k] > sol[i][1][t+k+r])
sol[i][1][t+k+r] = aux[1][t]+sol[g][1][k];
}
}
}
int main() {
scanf ("%d %d", &n, &x);
int i;
for(i=1; i<=n; i++) {
scanf("%d %d", &val[i], &par[i]);
Vadd(par[i],i);
}
parc(0);
for(rez=-1,i=0;i<=n;++i)
if(sol[0][0][i] >= x || sol[0][1][i] >= x)
rez=i;
printf("%d\n",rez);
return(0);
}