BSOJ: 3748 【USACO 2006 March Gold】Milk Team Select產奶比賽

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)ji(i)jflag=1flag=0

然後DP方程爲:
F[v][j][flag]=max(F[v][j][1],F[v][jk][1]+F[p][k1][1])max(F[v][j][1],F[v][jk][1]+F[p][k][0])max(F[v][j][0],F[v][jk][0]+max(F[p][k][0],F[p][k][1]))(flag=1,k1)(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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章