【动态规划】【图论最长路】[NOIP模拟赛]益智游戏

题目描述
小P 和小R 在玩一款益智游戏。游戏在一个正权有向图上进行。 小P 控制的角色要从A 点走最短路到B 点,小R 控制的角色要从C 点走最短路到D 点。 一个玩家每回合可以有两种选择,移动到一个相邻节点或者休息一回合。 假如在某一时刻,小P 和小R 在相同的节点上,那么可以得到一次特殊奖励,但是在每 个节点上最多只能得到一次。 求最多能获得多少次特殊奖励

输入
5 5
1 2 1
2 3 2
3 4 4
5 2 3
5 3 5
1 3 5 4
输出
2

首先可以预处理A,B,C,D分别为原点的最短路,然后可以发现如果一个点t在A->B的最短路上那么disa(t)+disb(t)=disa(b)那么这个时候发现在A->B和C->D的最短路上最多只有一段路能够重复,如果存在两端那么第一段的结尾到第二段的开始中间的应该是一样的,所以只要处理出所有的A->B且C->D中都经过的路,然后建立新的图,在图中搞拓扑排序,然后处理处最长路就行了(如果有相交那么答案要+1,因为相交存在最优解为1,但是最长路是0)

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 50000;
const int MAXM = 200000;
typedef long long LL;
const LL INF = 0x7f7f7f7f7f7f7f7fLL;
struct node{
    LL w;
    int v;
    node *next;
}Edges[MAXM+10], Edges2[MAXM+10], *ecnt=Edges, *adj[2][MAXN+10], *ecnt2=Edges2;
node Edges3[MAXM+10], *adj3[MAXN+10], *e3=Edges3;
int ct[MAXN+10];
void adde(int u, int v){
    ++e3;
    e3->v = v;
    e3->next = adj3[u];
    adj3[u] = e3;
}
int n, m, a, b, c, d;
LL di[4][MAXN+10];
int dp[MAXN+10];
bool vis[MAXN+10];
int Max(){
    queue<int> que;
    for(int i=1;i<=n;i++) if(!ct[i]) que.push(i);
    int ret = 0;
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(node *p=adj3[u];p;p=p->next){
            dp[p->v] = max(dp[p->v], dp[u]+1);
            ret = max(ret, dp[p->v]);
            ct[p->v] -- ;
            if(!ct[p->v]) que.push(p->v);
        }
    }
    return ret;
}
void addedge(int u, int v, LL w){
    ++ecnt;
    ecnt->v = v;
    ecnt->w = w;
    ecnt->next = adj[0][u];
    adj[0][u] = ecnt;

    ++ecnt2;
    ecnt2->v = u;
    ecnt2->w = w;
    ecnt2->next = adj[1][v];
    adj[1][v] = ecnt2;
}
void dij(int s, LL *dis, int f){
    for(int i=1;i<=n;i++) dis[i] = INF;
    dis[s] = 0;
    memset(vis, 0, sizeof vis);
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > que;
    que.push(make_pair(dis[s], s));
    vis[s] = true;
    while(!que.empty()){
        pair<int, int> u = que.top(); que.pop();
        vis[u.second] = true;
        for(node *p=adj[f][u.second];p;p=p->next){
            if(!vis[p->v] && dis[p->v] > dis[u.second] + p->w){
                dis[p->v] = dis[u.second] + p->w;
                que.push(make_pair(dis[p->v], p->v));
            }
        }
    }
}
int main(){
    int u, v;
    LL w;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++){
        scanf("%d%d", &u, &v);
        cin>>w;
        addedge(u, v, w);
    }
    scanf("%d%d%d%d", &a, &b, &c, &d);
    dij(a, di[0], 0); dij(b, di[1], 1);
    dij(c, di[2], 0); dij(d, di[3], 1);
    if(di[0][b] == INF || di[2][d] == INF){
        printf("-1\n");
        return 0;
    }
    bool have = false;
    for(int i=1;i<=n;i++)
        if(di[0][i] + di[1][i] == di[0][b] && di[2][i] + di[3][i] == di[2][d]){
            have = true;
            break;
        }
    for(int i=1;i<=n;i++){
        for(node *p=adj[0][i];p;p=p->next){
            v = p->v;
            if(di[0][i]+p->w+di[1][v]==di[0][b] && di[2][i]+p->w+di[3][v]==di[2][d]){
                ct[v]++;
                adde(i, v);
            }
        }
    }
    printf("%d\n", Max() + int(have));

    return 0;
}

转载于:https://www.cnblogs.com/JeremyGJY/p/5921658.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值