题目网址:https://ac.nowcoder.com/acm/problem/54148

题目描述:

Venn想要收集一些货物。Venn有一颗n个节点的树,一开始Venn在1号节点,其他每个节点都有一定的货物储备,Venn只要经过那些节点,就可以收集到节点的所有货物。每个节点的货物只能收集一次。
显然,Venn并不能轻易的收集所有的货物。每一条连接着两个节点的路径,都有一个邪恶的怪物镇守。Venn的武力值必须不小于怪物的武力值才能安全地从这条路径上通过。
Venn一开始的武力值是0,但是她可以选择健身来提升自己的武力值。每健身一分钟,就会提升一点武力值。Venn并不想收集所有的货物,只要最终收集到的货物总量不低于W就可以了。Venn一旦开始收集,就不能再健身了。但是Venn的速度很快,可以认为收集货物和从路径上经过都不需要时间。
由于Venn还急着去颓废,所以她想让你帮她计算收集到指定数量的货物最少需要几分钟。

输入描述:

一行两个正整数n,W。
接下来一行,有n-1个正整数,第i个数字a_i
表示编号为i+1节点的货物储备。
接下来n-1行,每行有三个正整数u,v,w,表示有一条路径链接编号为u,v的节点,并且路径上有一个武力值为w的怪物。

输出描述:

一行一个整数,表示最小时间花费。

示例

输入
4 7
5 5 2
1 3 2
1 2 7
1 4 5

输出
5

题解:

venn的武力值一定在[1,1e9],所以只需要对这个范围使用二分法,最坏的情况是分了log2(1e9)
次,每次需要进行的操作时,判断在当前武力值下是否可以收集到w的货物。
每次的操作思想从节点1开始,遍历与它相连的每一个结点,如果可以到达,那么将这个结点放入
队列中,如此往复循环,知道队列为空,或者收集到的货物大于等于为止。
需要注意的一点是:遍历过的结点需要进行标记,当下次通过其他结点可以到达这个结点时,自动
放弃当前结点,如果不这么做。结果也是对的,只不过会出现时间超时。

AC代码

#include"iostream"
#include"cstring"
#include"vector"
#include"queue"
using namespace std;
const int maxn =1000005;
struct node{
    int yy,vaa;
};
long long int n,w;
bool Is_no(int x); 
vector<struct node>a[maxn];
int v[maxn];
int p[maxn];
int main(){
    cin>>n>>w;
    for(int i=2;i<=n;i++) cin>>v[i];
    struct node temp;
    for(int i=1;i<n;i++){
        int x,y,va;
        cin>>x>>y>>va;
        temp.yy=y,temp.vaa=va;
        a[x].push_back(temp);//存储:所有与结点x相连的结点及其之间的武力值 
        temp.yy=x;
        a[y].push_back(temp);
    }
    int l=1,r=1000000000;//典型的二分 
    while(l<=r){
        int mid=(l+r)/2;
        if(Is_no(mid)) l=mid+1;
        else r=mid-1;
    }
    cout<<r+1;
    return 0;
} 
bool Is_no(int x){//如果武力值x满足,那么返回false,否则返回true 
    memset(p,0,sizeof(p));
    long long int s=0;
    p[1]=1;
    queue<int>q;
    q.push(1);//使用队列存储经过的结点的编号 
    while(!q.empty()){
        int m=q.front();q.pop();
        int size=a[m].size();
        for(int i=0;i<size;i++){
            if(!p[a[m][i].yy]&&a[m][i].vaa<=x){
                    s+=v[a[m][i].yy],q.push(a[m][i].yy);
                    p[a[m][i].yy]=1;//对遍历过的结点标记 
            }
        }
        if(s>=w) break;
    }
    if(s>=w) return false;
    else return true;
}

注:

本文章由白菜茄子原创,如有引用,请注明出处。

打卡第一天 2020/03/12 星期四