nyoj-307 宝物【dp+贪心】

宝物

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 6
描述

传说HMH大沙漠中有一个迷宫,里面藏有许多宝物。迷宫里可能有N个藏宝地点,用1到N标记。藏宝地点之间最多有一条通路相连。标记1为迷宫的进出口。

某天,Dr.Kong找到了迷宫的地图,他已经知道其中K(1<=K<=N)个不同的地点真的藏有宝物。Dr.Kong决定让他的机器人卡多去探险。卡多在经过某个藏宝地点时可能会拿走宝物。但它每拿走一个藏宝地点的宝物后,它的载重量就会增加W。迷宫中的通路不是平坦的,到处都是陷阱。假设每条通路都有一个危险度,其值与通过此路的载重量成正比。

机器人卡多进入迷宫时,它的载重量为0。只有当卡多携带宝物的载重量不大于某个通路的危险度时,它才能顺利通过此条道路,否则就会掉入陷阱,不能出来。

Dr.Kong希望他的机器人卡多尽量多的带出宝物,当然他更希望卡多最后能从标记1的地点走出去。

请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少个藏宝地点的宝物。

输入
有多组测试数据,以EOF为输入结束的标志
第1行: N M K W
接下来有K行, 每行一个整数,表示藏有宝物的地点标号。
再接下来有M行,每行三个整数X,Y,Z,表示地点X与地点Y之间有一条危险度为Z的通路。
1 ≤ N ≤ 8000 1 ≤ K ≤ N 1 ≤ M ≤ 15000 1 ≤ W, Z ≤ 10000
数据保证所有的地点之间都是有道路可以到达的。
提示:机器人卡多经过一个藏宝地点时可以不拿走宝物, 而且同一个藏宝地点可以经过多次。
输出
输出有一个整数, 表示卡多最多能带出的宝物的堆数。
样例输入
6 7 5 1
1
2
3
4
5
1 2 3
3 6 2
6 2 10
2 4 1
5 1 1
4 5 1
1 6 1
样例输出
4

我最开始想的是用搜索解决,但是数据量太逼大了。所以果断放弃。

分析题目:

(1)每个藏有宝藏的节点你要保证拿了之后能回到1节点。

(2)尽快能的多拿


所以我想用动态规划来处理得到:每个节点到1节点路径的危险度限制最大是多少。

限制是由路径中最小的那条边来表示。maxPass[u]实质就是记录从1->u权值最小的边。对于maxPass[v]就只记录限制最大的情况。

所以的状态转移方程:maxPass[v]=findMax(maxPass[v],findMin(maxPass[u],z)); 写起来很像spfa额。。。。Orz


接下来就是进行排序,用贪心的思想去多多的拿取。还是在代码中理解吧。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#define Max_V 8100
#define inf 0x3f3f3f3f
using namespace std;
int n,m,k,w;
struct Edge{
	int x,y,z;
};
vector<Edge> g;
bool have[Max_V];//标记该节点是否有宝藏
int maxPass[Max_V];//<span style="font-size: 13.3333339691162px;">每个节点到1节点路径的危险度限制最大是多少</span>
int ik[Max_V];//有效的宝藏点
int p[Max_V];//贪心用的数组
int findMax(int a,int b)
{
	return a>b?a:b;
}
int findMin(int a,int b)
{
	return a>b?b:a;
}
void init()
{
	g.clear();
	memset(have,0,sizeof(have));
}
void add_edge(int x,int y,int z)
{//无向图
	g.push_back((Edge){x,y,z});
	g.push_back((Edge){y,x,z});
}
int cmp(const void *a,const void *b)
{
	return *(int *)a-*(int *)b;
}
int find_way()
{//动态规划找到每个节点到1节点<span style="font-size: 13.3333339691162px;">路径的危险度限制最大是多少</span>
	memset(maxPass,0,sizeof(maxPass));
	maxPass[1]=inf;
	bool flag=true;
	int u,v,z;
	while(flag)
	{
		flag=false;//当遍历所有边都没有maxPass数组更新时,说明动规结束
		for(int i=0;i<g.size();i++)
		{
			u=g[i].x;v=g[i].y;z=g[i].z;
			int t=maxPass[v];
			maxPass[v]=findMax(maxPass[v],findMin(maxPass[u],z));
			if (maxPass[v]!=t)//如果有一个节点的maxPass数组有更新,那么这个节点就可能去更新其它节点。所以动规不终止
				flag=true;
		}
	}
	int count=0;
	for(int j=1;j<=n;j++)
		if(have[j]&&maxPass[j]>=w)//将藏有宝藏的节点记录
			ik[count++]=maxPass[j];
	return count;
}
int solve()
{
	int mk=find_way();
	p[0]=1;
	qsort(ik,mk,sizeof(int),cmp);//排序后贪心
	for(int i=1;i<mk;i++)
	{
		if(ik[i]>=p[i-1]*w+w)
			p[i]=p[i-1]+1;
		else p[i]=p[i-1];
	}
	return p[mk-1];
}
int main()
{
	int i;
	while(~scanf("%d%d%d%d",&n,&m,&k,&w))
	{
		init();
		int loc;
		for(i=0;i<k;i++)
		{
			scanf("%d",&loc);
			have[loc]=1;
		}
		int x,y,z;
		for(i=0;i<m;i++)
		{
			scanf("%d%d%d",&x,&y,&z);
			add_edge(x,y,z);
		}
		printf("%d\n",solve());
	}
	return 0;
}


你可能感兴趣的:(dp)