题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1142
题意:有n个点,m条边带权的无向图,点1为起点,点2为终点。求点1到点2有几条路,路的限制是每过一个点,都要后点到2的距离小于前点的距离。
思路:先用spfa/dijkstra算一遍每个点到2的最短距离。再搜索一下符合题意的路,普通的dfs是肯定超时的。这里用到记忆化搜索,用到动态规划的思想,dp[i]记录点i到2有几条符合题意的路。当搜索到已经搜索过的点,直接加就可以了,不用再搜。比如:3到2有2条路(即dp[3]=2),搜索4时,4和3连通,再搜索3时,dp[4]+=dp[3]。就不用重复操作。
记忆化搜索代码:
int dfs(int k){//返回几条符合的路 if(dp[k] > 0) return dp[k];//dp初始值为0,已经搜索过,直接返回dp值 if(k == 2) return 1;//搜索终点,搜索到2,说明这是条符合的路, for(int i = head[k]; i != -1 ;i = edge[i].next){// 查找连通的点 int v = edge[i].to; if(d[k] > d[v])//符合要求,继续搜索直到搜索到2或已经搜索过 dp[k] += dfs(v); } //cout<<k<<" "<<dp[k]<<endl; return dp[k]; }
完整代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=1e3+10; struct node{ int from,to,w,next; }edge[maxn*maxn]; int d[maxn],head[maxn*maxn],visit[maxn],dp[maxn]; int n,m,cnt; void init(){//初始化 memset(head,-1,sizeof(head)); memset(d,0x3f,sizeof(d)); memset(visit,0,sizeof(visit)); memset(dp,0,sizeof(dp)); cnt=0; } void add(int u,int v,int w){//前向星连边 edge[cnt].from=u; edge[cnt].to=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; } int dfs(int k){//返回几条符合的路 if(dp[k] > 0) return dp[k];//dp初始值为0,已经搜索过,直接返回dp值 if(k == 2) return 1;//搜索终点,搜索到2,说明这是条符合的路, for(int i = head[k]; i != -1 ;i = edge[i].next){// 查找连通的点 int v = edge[i].to; if(d[k] > d[v])//符合要求,继续搜索直到搜索到2或已经搜索过 dp[k] += dfs(v); } //cout<<k<<" "<<dp[k]<<endl; return dp[k]; } void spfa(){//spfa求到2的最短路 queue<int> q; q.push(2); visit[2]=1; d[2]=0; while(!q.empty()){ int u=q.front(); q.pop(); visit[u]=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; int w=edge[i].w; if(d[v]>d[u]+w){ d[v]=d[u]+w; if(!visit[v]){ visit[v]=1; q.push(v); } } } } } int main(){ while(scanf("%d",&n)!=EOF&&n){ init(); scanf("%d",&m); int u,v,w; for(int i=0;i<m;i++){ scanf("%d%d%d",&u,&v,&w); add(u,v,w);//无向图 add(v,u,w); } spfa(); printf("%d ",dfs(1)); } return 0; }