【题目描述】
绿猪们的势力越来越强大了,小鸟们正在筹划如何对付这群强大的东西。
可是绿猪们派来一位绿猪巫师洗清了所有前线鸟儿的记忆。现在,他们只记得他们各自的领导,但是这是一个自由散漫的团队,必须要找到他们的鸟队长。(鸟队长是能管理所有的鸟儿作战,鼓舞士气,勇于拼搏在前线的英雄鸟)。
你是在后方暗地操作的司令,现有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; }