传说HMH大沙漠中有一个迷宫,里面藏有许多宝物。迷宫里可能有N个藏宝地点,用1到N标记。藏宝地点之间最多有一条通路相连。标记1为迷宫的进出口。
某天,Dr.Kong找到了迷宫的地图,他已经知道其中K(1<=K<=N)个不同的地点真的藏有宝物。Dr.Kong决定让他的机器人卡多去探险。卡多在经过某个藏宝地点时可能会拿走宝物。但它每拿走一个藏宝地点的宝物后,它的载重量就会增加W。迷宫中的通路不是平坦的,到处都是陷阱。假设每条通路都有一个危险度,其值与通过此路的载重量成正比。
当机器人卡多进入迷宫时,它的载重量为0。只有当卡多携带宝物的载重量不大于某个通路的危险度时,它才能顺利通过此条道路,否则就会掉入陷阱,不能出来。
Dr.Kong希望他的机器人卡多尽量多的带出宝物,当然他更希望卡多最后能从标记1的地点走出去。
请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少个藏宝地点的宝物。
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; }