4102: [Usaco2015 Open]Bessie
Description
为了庆祝贝茜的生日,FJ给她吃草的自由. N块草地,标号1到N(1<=N<=1000),草地有营养价值.当贝茜走到这个草地,可以获得等于这块草地的营养价值的能量. 每块草地最多有10条双向边,每走一条边,贝茜花费E的能量. 贝茜拿可以从任何地方出发,当她不能获得更多的能量的时候她就会停止. 然而因为贝茜挑食,她每次不会吃低于或等于上次的营养价值的草地,所以她获得的能量是严格上升的,当然她可以选择只是经过一块草地而不吃草.算出贝茜能够获得的最大能量.
Input
第一排两个数 N和E
剩下N排,每排包含Q,D两个数字,Q代表这个草地的营养价值,D代表这个点的边数,剩下D个数表示于这块草地相邻的草地.
Output
一个整数,最大权值
Sample Input
5 2
4 1 2
1 3 1 3 4
6 2 2 5
5 2 2 5
2 2 3 4
Sample Output
7
【解题报告】
先以每个点为起点跑一次BFS,求出任意两点间的最短距离dis[i][j]。
再将所有点按权值从小到大排序,按这个顺序DP。
状态转移方程:
dp[j]=maxdp[k]+a[j]−dis[k][j],(1≤k≤j−1)
。最后在dp数组中求最大值,即为答案。
代码如下:
/**************************************************************
Problem: 4102
User: onepointo
Language: C++
Result: Accepted
Time:540 ms
Memory:4920 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 1010
int dis[N][N],vis[N];
int n,E,cnt=-1,head[N];
struct Edge{int to,nxt;}e[N<<3];
struct Data
{
int val,id;
bool friend operator < (Data a,Data b)
{return a.val<b.val;}
}a[N];
int dp[N];
void adde(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void bfs(int s)
{
queue <int> q;
memset(dis[s],-1,sizeof(dis[s]));
memset(vis,0,sizeof(vis));
vis[s]=1;q.push(s);dis[s][s]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];~i;i=e[i].nxt)
{
int v=e[i].to;
if(!vis[v])
{
dis[s][v]=dis[s][u]+E;
vis[v]=1;
q.push(v);
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&E);
for(int i=1,d;i<=n;++i)
{
scanf("%d%d",&a[i].val,&d);
a[i].id=i;
for(int j=1,x;j<=d;++j)
{
scanf("%d",&x);
adde(i,x);
}
}
for(int i=1;i<=n;++i) bfs(i);
sort(a+1,a+n+1);
int ans=0;
for(int i=1;i<=n;++i)
{
dp[i]=a[i].val;
for(int j=1;j<i;++j)
if(dis[a[i].id][a[j].id]>=0&&a[i].val>a[j].val)
dp[i]=max(dp[i],dp[j]+a[i].val-dis[a[i].id][a[j].id]);
ans=max(ans,dp[i]);
}
printf("%d",ans);
return 0;
}