题目链接
题意:给定n个点,m条边,1号点是起点,2号点是终点,问从起点到终点满足条件的路有多少条,这个“条件”如下:
He considers taking a path from A to B to be progress if there exists a route from B to his home that is shorter than any possible route from A.
如果你选择要走(A,B)这条路,那么必须保证从B到终点有一条路小于从A到终点的所有路。
(キ`゚Д゚´)!! 突然想起来孔老师说过的,对于任意x1, 存在x2,使 B(x2) < A(x1) 等价于 函数B的最小值小于函数A的最小值,即dist[B]<dist[A]。很简单的逻辑,在这里一时没反应过来...
用Dijkstra求终点2到其他所有点的最短路(处理出dist函数),然后记忆化搜索。
其实我还不太会写记忆化搜索,但是可以看这里。
代码如下:
法一:朴素Dijkstra+邻接矩阵(62ms 5.3MB)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005,M=1000005,INF=0x3f3f3f3f;
int g[N][N];
int dist[N],dp[N];//dp[]经过此点的合法路径数量
bool st[N];
int n,m,ans;
void Dij(int s){
memset(dist,INF,sizeof dist);
dist[s]=0;
for(int i=1;i<n;++i){
int t=-1;
for(int j=1;j<=n;++j) if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j;
st[t]=true;
for(int j=1;j<=n;++j) dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
int dfs(int x){//记忆化搜索
if(x==2) return 1;//如果到达了2,那么就算是一条路径
if(dp[x]!=0) return dp[x];
int cnt=0;
for(int i=1;i<=n;++i)
if(g[x][i]!=INF&&dist[x]>dist[i]) cnt+=dfs(i);
return dp[x]=cnt;
}
int main(){
while( ~scanf("%d",&n) && n ){
memset(g,0x3f,sizeof g);
memset(st,0,sizeof st);
memset(dp,0,sizeof dp);
ans=0;
scanf("%d",&m);
for(int i=0;i<m;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a][b]=g[b][a]=c;
}
Dij(2);
cout<<dfs(1)<<"\n";//搜索符合条件的路
}
}
法二:
堆优化版Dijkstra+邻接表(31ms 1.8MB)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005,M=1000005,INF=0x3f3f3f3f;
typedef pair<int,int>PII;
int h[N],e[M],ne[M],w[M],idx;
int dist[N],dp[N];//dp[]经过此点的合法路径数量
bool st[N];
int n,m,ans;
void add(int a,int b,int c){
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void Dij(int s){
memset(dist,INF,sizeof dist);
dist[s]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap;
heap.push({0,s});
while(heap.size()){
auto t=heap.top();
heap.pop();
int ver=t.second,distance=t.first;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];~i;i=ne[i]){
int j=e[i];
if(dist[j]>distance+w[i]){
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
}
int dfs(int x){//记忆化搜索
if(x==2) return 1;//如果到达了2,那么就算是一条路径
if(dp[x]!=0) return dp[x];
int cnt=0;
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(w[i]!=INF&&dist[x]>dist[j]) cnt+=dfs(j);
}
// for(int i=1;i<=n;++i)
// if(g[x][i]!=INF&&dist[x]>dist[i]) cnt+=dfs(i);
return dp[x]=cnt;
}
int main(){
while( ~scanf("%d",&n) && n ){
memset(h,-1,sizeof h);
memset(st,0,sizeof st);
memset(dp,0,sizeof dp);
ans=idx=0;
scanf("%d",&m);
for(int i=0;i<m;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
Dij(2);
cout<<dfs(1)<<"\n";//搜索符合条件的路
}
}
法三:SPFA+邻接表(31ms 1.8MB)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005,M=1000005,INF=0x3f3f3f3f;
typedef pair<int,int>PII;
int h[N],e[M],ne[M],w[M],idx;
int dist[N],dp[N];//dp[]经过此点的合法路径数量
bool st[N];
int n,m,ans;
void add(int a,int b,int c){
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void SPFA(int s){//无脑默写
memset(dist,INF,sizeof dist);
dist[s]=0;
queue<int>q;
q.push(s);
st[s]=true;//入队啦
while(q.size()){
int t=q.front();
q.pop();
st[t]=false;//不在队中
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
int dfs(int x){//记忆化搜索
if(x==2) return 1;//如果到达了2,那么就算是一条路径
if(dp[x]!=0) return dp[x];
int cnt=0;
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(w[i]!=INF&&dist[x]>dist[j]) cnt+=dfs(j);
}
// for(int i=1;i<=n;++i)
// if(g[x][i]!=INF&&dist[x]>dist[i]) cnt+=dfs(i);
return dp[x]=cnt;
}
int main(){
while( ~scanf("%d",&n) && n ){
memset(h,-1,sizeof h);
memset(st,0,sizeof st);
memset(dp,0,sizeof dp);
ans=idx=0;
scanf("%d",&m);
for(int i=0;i<m;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}
SPFA(2);
cout<<dfs(1)<<"\n";//搜索符合条件的路
}
}