题目:益智游戏
(!!!本题很卡时间!!!)
题目描述:
小 P 和小 R 在玩一款益智游戏。游戏在一个正权有向图上进行。
小 P 控制的角色要从 A 点走最短路到 B 点,小 R 控制的角色要从 C 点走最短路到 D 点。
一个玩家每回合可以有两种选择,移动到一个相邻节点或者休息一回合。
假如在某一时刻,小 P 和小 R 在相同的节点上,那么可以得到一次特殊奖励,但是在每个节点上最多只能得到一次。
求最多能获得多少次特殊奖励。
输入:
第一行两个整数 n,m 表示有向图的点数和边数。
接下来 m 行每行三个整数 xi,yi,li,表示从 xi到 yi有一条长度为 li的边。
最后一行四个整数 A,B,C,D,描述小 P 的起终点,小 R 的起终点。
输出:
一个整数表示最多能获得多少次特殊奖励。若小 P 不能到达 B 点或者小 R 不能到达 D点则输出-1。
样例输入:
5 5
1 2 1
2 3 2
3 4 4
5 2 3
5 3 5
1 3 5 4
样例输出:
2
简析:
首先,要保证两人都是走的最短路,如果不能到达则输出-1。
然后我们从所有的点中筛选出被两条最短路同时包含的点,再在原图的基础上将这些点建一个新图,用拓扑排序跑一遍(找出最多的公共点,即最长路)。
以上是大致思路。
接下来逐个解决几个问题:
1.Q:跑最短路需要注意什么?
A:跑最短路最好用算法:Dijkstra+优先队列优化。
2.Q:如何找出哪些点同 时在两条最短路中?
A:首先用静态链接表建好图(正反都要建),然后跑四遍最短路,从起点A到终点B,从终点B到起点A,从起点C到终点D,从终点D到起点C,同时用四个数组dis记录下每个点到这四个殊点的最短距离。假设存在一条u到v的边(u指向v),如果A到u的距离+v到B的距离+u到v的距离==A到B的距离,那么u,v两点在A到B最短路中,若同理判 断出u,v同时在C到D的最短路上,那么 v的入度+1,(什么是入度?拓扑排序的内容,不会的话去学习一波),并且将u,v加入新图中(加入什么新图?也是拓扑排序要用的)。
3.Q:关DP什么事儿?
A:我也不太清楚,不过似乎好像大约用到了。
4.Q:还有什么要需要注意的吗?
A:这题要开很多东西,先想清楚在动手,不然就像我一样,用生命去改代码。
代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int inf=0x3f3f3f3f;
inline int scan(){ //读入优化
int x=0;
char c=getchar();
while(c>'9'||c<'0')
c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+c-'0',
c=getchar();
return x;
}
struct node{
int to,next,dis;
};
node edge[201000][2]; //用于双向建图
int pre[51000][2],edge_num1,dis[5][51000],edge_num2; //双向建图
int m,n,into[51000],dp[51000]; //拓扑排序
inline void add1(const int &x,const int &y,const int &z){
edge[++edge_num1][0].next=pre[x][0];
edge[edge_num1][0].to=y;
edge[edge_num1][0].dis=z;
pre[x][0]=edge_num1; //正向建图
edge[++edge_num2][1].next=pre[y][1];
edge[edge_num2][1].to=x;
edge[edge_num2][1].dis=z;
pre[y][1]=edge_num2; //反向建图
}
struct node3{
int num,dis;
};
struct comp{
bool operator()(node3 x,node3 y){
return x.dis>y.dis;
}
};
priority_queue<node3,vector<node3>,comp> q; //Dijkstra的优先队列优化
inline void Dijk(int l,int x,int d[]){ //不会Dijkstra?去学习!
node3 a;
a.num=x;a.dis=0; // .num记录节点编号, .dis记录到源点的最短距离
q.push(a);
d[x]=0;
while(!q.empty()){
int u=q.top().num;
q.pop();
for(int i=pre[u][l];i;i=edge[i][l].next){
int v=edge[i][l].to;
if(d[u]+edge[i][l].dis<d[v]){
d[v]=d[u]+edge[i][l].dis;
node3 a;
a.num=v;a.dis=d[v];
q.push(a);
}
}
}
}
int pret[51000],edge_numt; //建新图
node edget[201000]; //建新图
inline void add2(const int &x,const int &y){
edget[++edge_numt].next=pret[x];
edget[edge_numt].to=y;
pret[x]=edge_numt;
} //将同时在两条最短路的点加入新图
int ans=0;
inline void topord(){ //拓扑排序
queue<int>q;
for(int i=1;i<=n;i++)
if(!into[i]){
q.push(i);
dp[i]=1;
}
while(!q.empty()){
int x=q.front();
q.pop();
ans=max(ans,dp[x]);
for(int i=pret[x];i;i=edget[i].next){
int v=edget[i].to;
dp[v]=max(dp[v],dp[x]+1);
into[v]--;
if(!into[v])
q.push(v);
}
}
}
int main(){
n=scan();m=scan();
for(int i=1;i<=m;i++){
int x=scan(),y=scan(),z=scan();
add1(x,y,z);
}
memset(dis,inf,sizeof(dis));
int a=scan(),b=scan(),c=scan(),d=scan();
Dijk(0,a,dis[1]);
Dijk(1,b,dis[2]);
Dijk(0,c,dis[3]);
Dijk(1,d,dis[4]); //将四个点分别作为源点求最短路,并将距离储存在各自的数组中
if(dis[1][b]>=inf||dis[3][d]>=inf){
printf("-1"); //无法到达
return 0;
}
for(int x=1;x<=n;x++){
for(int i=pre[x][0];i;i=edge[i][0].next){
int v=edge[i][0].to;
if(dis[1][x]>=inf||dis[2][v]>=inf||dis[3][x]>=inf||dis[4][v]>=inf)
continue;
if(dis[1][x]+edge[i][0].dis+dis[2][v]==dis[1][b]&&dis[3][x]+edge[i][0].dis+dis[4][v]==dis[3][d]){ //判断是否同时在两条最短路中(别搞错正向和反向)
add2(x,v);
into[v]++;
}
}
}
topord();
printf("%d",ans);
}
这篇可能会超时,,,