【图论】Angry Birds——BSOJ 2900

【题目描述】

绿猪们的势力越来越强大了,小鸟们正在筹划如何对付这群强大的东西。

可是绿猪们派来一位绿猪巫师洗清了所有前线鸟儿的记忆。现在,他们只记得他们各自的领导,但是这是一个自由散漫的团队,必须要找到他们的鸟队长。(鸟队长是能管理所有的鸟儿作战,鼓舞士气,勇于拼搏在前线的英雄鸟)。

你是在后方暗地操作的司令,现有M(1 <= M <= 500)件装备,每只不同的鸟儿可以分配零件至M件装备,分配到不同的装备数量会有不同的战斗力。

绿猪们也不是吃素的,他们规划了一个方案,站成一字长蛇阵在草地上摆开阵势,静候着小鸟们。

此时,你也只好得选出一条长链来应战绿猪。(温馨提示:小鸟的编号从1开始)

现在,你的任务有三:

一) 输出鸟队长的编号

二) 输出将M件装备分配后整个团队的战斗力的最大值

三) 以鸟队长为首的最大战斗力的战斗长链的鸟儿的序列

【输入格式】

第一行包含两个整数,N,M(N表示小鸟的数量,1 <= N <= 100) 第2行至N+1行,每行M+1个整数:第一个整数表示谁指挥这只鸟儿,第2至M+1个整数表示这只鸟儿分配I件装备所获得的战斗力

【输出格式】

输出有三行:第一行输出鸟队长的编号,第二行输出将M件装备分配后整个团队的战斗力的最大值,第三行输出以鸟队长为首的战斗长链的节点序列。(按字典序最小输出,具体格式请参考样例输出) 注意如果第一问无解,则输出“not found”,后面不输出。

【输入样例】

3 2
0 2 1
1 1 3
1 3 1

【输出样例】

1
5
1->3

【题目解析】

对于本题的第一问,解法有多种,可供大家选择,最简单的方法是扫描所有结点一遍,找到入度为 0 的点。或者是用 Floyd 求出传递闭包(有向图),当且仅当一个点能够访问到所有结点时,即为本问解。

然而第二问经过分析,则是一个简单的资源分配类DP,即将 M 件装备分配给 N 只鸟儿所获得的最大战斗力。

容易列出方程:F[i,j]=max{F[i,j],F[k,j-1]+A[j,i-k]}(0<=k<=i)F[i,j]表示i件装备分发给前j只鸟所获取的最大战斗力和,A[i,j]表示第i只鸟得到j件装备可以获得的战斗力。

第三问是求树的最长单链,简单的树形动态规划法或者搜索就能解决。由于是字典序最小,所以严格控制好判定方向即可F[i]=F[k]+A[i]。

【代码】

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read(){//快读 
    int s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
    return s*f;
}
int n,m,cnt,h[105],p[105][505],f[105][505],num[105],dis[105];
bool s[105][505][505];//s[i][j][k]标记:在二问最大战斗力的情况下共j个装备第i只鸟是否获得了k个装备 
struct edge{//前向星,即邻接表 
    int to,next;
}d[205];
void hqq_add(int x,int y){
    cnt++;
    d[cnt].to=y;
    d[cnt].next=h[x];
    h[x]=cnt;
}
struct data{//广搜中每种情况的状态 
    int sum,l,ans[105],x;//x为当前位置,ans存当前路径,sum存路径长度 
}c,main_ans;
queue<data>q;//用于广搜 
int main(){
    int i,j,k;
    n=read();m=read();
    for(i=1;i<=n;i++){
        j=read();
        if(j)hqq_add(j,i);
        else{//找到鸟队长 因为鸟队长不被管理 
            printf("%d\n",i);
            c.x=i;
        }
        for(j=1;j<=m;j++){
            p[i][j]=read();
        }
    }
    if(!c.x){//注意如果第一问无解,则输出“not found”,后面不输出。
        printf("not found");
        return 0;
    }
    for(i=1;i<=n;i++){//多重背包 
        for(j=1;j<=m;j++){
            f[i][j]=f[i-1][j];
            for(k=1;k<=j;k++){
                if(f[i][j]<f[i-1][j-k]+p[i][k]){
                    f[i][j]=f[i-1][j-k]+p[i][k];
                    s[i][j][k]=1;
                }
            }
        }
    }
    printf("%d\n",f[n][m]);//二问的答案 
    j=n;k=m;
    while(j!=0&&k!=0){//处理在二问最优情况下每只鸟的装备数量 
        for(i=k;i>=1;i--){
            if(s[j][k][i]){
                k-=i;
                num[j]=i;//num->装备数量 
                break;
            }
        }
        j--;
    }
    for(i=1;i<=n;i++){
        num[i]=p[i][num[i]];//num->每只鸟的战斗力 
    }
    c.sum=num[c.x];
    c.ans[++c.l]=c.x;
    q.push(c);
    while(q.size()){//进行广搜,寻找第三问的解 
        data now=q.front();
        q.pop();
        for(i=h[now.x];i;i=d[i].next){
            j=d[i].to;
            if(dis[j]<dis[now.x]+num[j]){//路径更优秀,更新(update) 
                data des;
                des.x=j;
                des.sum=dis[j]=dis[now.x]+num[j];
                memcpy(des.ans,now.ans,sizeof(now.ans));//复制之前的路径 
                des.l=now.l+1;//将当前位置存入路径中 
                des.ans[des.l]=des.x;
                q.push(des);
                if(des.sum>main_ans.sum){//main_ans用于存储最优值 
                    main_ans=des;
                }
            }
        }
    }
    printf("%d",main_ans.ans[1]);//输出最优路径即可 
    for(i=2;i<=main_ans.l;i++){
        printf("->%d",main_ans.ans[i]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kirito-hqq/p/9558117.html
今日推荐