path(CCPC网络赛)

path

Time Limit: 2000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
You have a directed weighted graph with  n vertexes and m edges. The value of a path is the sum of the weight of the edges you passed. Note that you can pass any edge any times and every time you pass it you will gain the weight.

Now there are q queries that you need to answer. Each of the queries is about the k-th minimum value of all the paths.
 
Input
The input consists of multiple test cases, starting with an integer  t (1t100), denoting the number of the test cases.
The first line of each test case contains three positive integers n,m,q. (1n,m,q5104)

Each of the next m lines contains three integers ui,vi,wi, indicating that the ith edge is from ui to vi and weighted wi.(1ui,vin,1wi109)

Each of the next q lines contains one integer k as mentioned above.(1k5104)

It's guaranteed that Σn ,ΣmΣq,Σmax(k)2.5105 and max(k) won't exceed the number of paths in the graph.
 
Output
For each query, print one integer indicates the answer in line.
 
Sample Input
1
2 2 2
1 2 1
2 1 2
3
4
Sample Output
3
3
Hint
1->2 value :1
2->1 value: 2
1-> 2-> 1 value: 3
2-> 1-> 2 value: 3

题意:n个点m条 边;q次询问;问在图中所有路径权值中第k小的值;
 
解法,把当前点和走到它时的边压入multiset中;
#include<bits/stdc++.h>
using namespace std;
const long long N=2e5+50;
struct nn  //用于临时存边,并对边的权值由小到大排序; 
{
    long long u,v,w;
 
    bool operator < (const nn & other_nn) const
    {
        return w<other_nn.w;
    }
};
 
vector<nn>a[N];
 
struct ss //前向星加边操作
{
    long long u,v,w,next;
}edg[N];

long long head[N],sumedg=0;
void addedg(long long u,long long v,long long w)
{
    //printf("%lld %lld %lld\n",u,v,w);    
    edg[sumedg]=(ss){u,v,w,head[u]};
    head[u]=sumedg++;
}

long long kmax=0,kk[N];
long long res[N]={0},sum=0;
long long n,m;
struct oo //定义一个结构体;item 表示最后到达的点;valu表示走过的边权和
{
    long long item,valu;
    
    bool operator < (const oo &other)const
    {
        return valu<other.valu;
    }
};
multiset<oo>q; //类似于优先队列;可以满足排序,从前后都可以删去;
void spfa(long long kmax)
{
//    printf("%lld(nn)\n",n); 
    q.clear();
    for(long long i=1;i<=n;i++) q.insert((oo){i,0}); //初始,把{i,0}加入,每个点,和0

    while(!q.empty())
    {
    /*    multiset<oo>::iterator it;
        for(it=q.begin();it!=q.end();it++)printf("%lld %lld | ",it->item,it->valu); //便利multiset
        printf("\n");
        */
        oo now=*q.begin();  //加* 表引用,赋值;
        q.erase(q.begin());//删除第一个
        
        //printf("%lld %lld %lld\n",now.item,now.valu,q.begin()->item);
        
        
        if(now.valu) //保证从小到大排序;使得每次拿出来的一定是第sum大的
        {
            res[sum++]=now.valu;
        }
        if(sum>=kmax) return;//如果应该找的kmax
        
        for(long long i=head[now.item];i!=-1;i=edg[i].next) //对当前点增广
        {
            q.insert((oo){edg[i].v,edg[i].w+now.valu});
            if(q.size()>kmax+n-sum+5) //一共有kmax+n个数;找到sum个;还需要kmax+n-sum;为了防止multiset爆;
            {
                q.erase(--q.end());//如果多了,就删除最后的大的
                if(now.valu+edg[i].w>(*(--q.end())).valu) break;//如果当前比最后的值都大;就没有必要向下找了; 
            }
        }
    }
}
int main()
{
    long long  t;
    scanf("%lld",&t);
    while(t--)
    {
        long long q; 
        sumedg=0;//初始化!!!
        sum=0;
        kmax=0;
        scanf("%lld%lld%lld",&n,&m,&q);
        for(int i=0;i<=n;i++)head[i]=-1,a[i].clear(); //初始化!!! 
        
        for(long long i=0;i<m;i++)
        {
            long long u,v,w;
            scanf("%lld %lld %lld",&u,&v,&w);
            a[u].push_back((nn){u,v,w}); //存起来
        }
 
        for(long long i=1;i<=n;i++)
        {
        //    printf("%lld:%lld\n",i,a[i].size());
            sort(a[i].begin(),a[i].end());//对vector按边权排序;
            for(long long j=a[i].size()-1;j>=0;j--) //反向加边;因为要找前K小的;而前向星又是越是后建的边,越先访问;
            addedg(a[i][j].u,a[i][j].v,a[i][j].w);
        }
        for(long long i=0;i<q;i++) //取K的max;使得有一个范围大小在;
        {
            scanf("%lld",&kk[i]);
            kmax=max(kmax,kk[i]);
        }
        
        spfa(kmax);
        
      //  for(long long i=0;i<10;i++)printf("%lld ",res[i]); 
        for(long long i=0;i<q;i++) printf("%lld\n",res[kk[i]-1]);//从0开始
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sylvia1111/p/11403763.html