Tyvj1139 向远方奔跑(APIO 2009 抢掠计划)

描述

    在唐山一中,吃饭是一件很令人头疼的事情,因为你不可能每次都站在队伍前面买饭,所以,你最需要做的一件事就是——跑饭。而跑饭的道路是无比艰难的,因为路是单向的(你要非说成是双向的我也没法,前提是你可以逆着2000+个狂热的跑饭群众而行),所以要合理选择路线。并且,在抵达你的目的地——板面馆之前,你需要先买一些干粮,比如烧饼之类的。现在给你每个干食商店的位置和干食喜爱度,请你设计一个跑饭方案使得在到达板面馆之前能得到尽可能多的干食喜爱度。

输入格式

    第一行包含两个整数N、M。N表示食品商店的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道 路的起点和终点的食品商店编号。接下来N行,每行一个整数,按顺序表示每个食品商店处的干食喜爱度。接下来一行包含两个整数S、P,S表示学校的编号,也就是出发地点。P表示板面馆数目。接下来的一行中有P个整数,表示P个卖板面的食品商店的编号。

输出格式

输出一个整数,表示到达板面馆之前能得到的最多的干食喜爱度。

测试样例1

输入

6 7 
1 2 
2 3 
3 5 
2 4 
4 1 
2 6 
6 5 
10 
12 

16 


1 4 
4 3 5 6

输出

47

备注

40%的数据n,m<=300   100%的数据n,m<=3000 

思路:

1、分析路可以重复走,想到强连通分量,如果强连通分量里面有一个点被访问,那么整个强连通分量都要被访问,否则答案偏小,于是先tarjan再缩点

2、缩点之后,整个图由一个有向有环图变为一个有向无环图,于是spfa处理,然后就AC了(bzoj上的好像能卡掉,WYW用tarjan写信息传递好像被卡了)

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#include<stack>
#define maxn 3005
#define maxint ~0U>>1
using namespace std;
stack<int> sta;
vector<int> comp[maxn];
int incomp[maxn],instack[maxn],dfn[maxn],low[maxn];
int index,cnum;
int n,m,q,d[maxn],rice[maxn],noodle[maxn],v[maxn],s,p,ans;
vector<int> g[maxn],nowg[maxn];
int nowrice[maxn],nownoodle[maxn],nows;
void input(){
    cin>>n>>m;
    int u,v;
    for(int i = 1;i <= m;i++){
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        
    }
    for(int i = 1;i <= n;i++){
        scanf("%d",&rice[i]);
    }
    cin>>s>>p;
    int nnow;
    for(int i = 1;i <= p;i++){
        scanf("%d",&nnow);
        noodle[nnow] = 1;
    }
}
void tarjan(int u){
    instack[u] = 2;
    dfn[u] = low[u] = ++index;
    sta.push(u);
    for(int i = 0;i < g[u].size();i++){
        int j = g[u][i];
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u],low[j]);
        }else if(instack[j] == 2){
            low[u] = min(low[u],dfn[j]);
        }
    }
    if(dfn[u] == low[u]){
        ++cnum;
        while(!sta.empty()){
            int t = sta.top();
            sta.pop();
            instack[t] = 1;
            incomp[t] = cnum;
            comp[cnum].push_back(t);
            if(t == u){
                break;
            }
        }
    }
}
void work(){
    for(int i = 1;i <= n;i++){
        if(!dfn[i]) tarjan(i);
    }
    int u,v,newu,newv;
    for(int i = 1;i <= n;i++){
        u = i;
        newu = incomp[u];
        nowrice[newu] += rice[u];
        if(noodle[u]) nownoodle[newu] = 1;
        if(u == s) nows = newu;
        for(int j = 0;j < g[u].size();j++){
            v = g[u][j];
            newv = incomp[v];
            if(newu == newv) continue;
            nowg[newu].push_back(newv);
        }
    }
}
void spfa(){
    queue<int> q;
    d[nows] = nowrice[nows];
    v[nows] = 1;
    q.push(nows);
    int xx,dd,to,wei;
    while(!q.empty()){
        xx = q.front();
        dd = d[xx];
        q.pop();
        v[xx] = 0;
        if(nownoodle[xx]) ans = max(ans,dd);
        for(int i = 0;i < nowg[xx].size();i++){
            to = nowg[xx][i];
            wei = nowrice[to];
            if(dd + wei > d[to]){
                d[to] = dd + wei;
                if(!v[to]){
                    v[to] = 1;
                    q.push(to);
                }
            }
        }
    }
    cout<<ans;
}
int main(){
    input();
    work();
    spfa();
    return 0;
}

 

转载于:https://www.cnblogs.com/hyfer/p/5812614.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值