宝物
-
描述
-
传说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
-
有多组测试数据,以EOF为输入结束的标志
我最开始想的是用搜索解决,但是数据量太逼大了。所以果断放弃。
分析题目:
(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;
}