邻接表用数组模拟+dij+优先队列,dfs剪枝+邻接表用数组模拟,链表+dij+优先队列

来源:互联网 发布:网络时时彩案件判刑 编辑:程序博客网 时间:2024/06/05 04:11

                     题目链接:http://poj.org/problem?id=1724

                                            ROADS

 POJ - 1724 


N cities named with numbers 1 ... N are connected with one-way roads. Each road has two parameters associated with it : the road length and the toll that needs to be paid for the road (expressed in the number of coins). 
Bob and Alice used to live in the city 1. After noticing that Alice was cheating in the card game they liked to play, Bob broke up with her and decided to move away - to the city N. He wants to get there as quickly as possible, but he is short on cash. 

We want to help Bob to find the shortest path from the city 1 to the city N that he can afford with the amount of money he has. 
Input
The first line of the input contains the integer K, 0 <= K <= 10000, maximum number of coins that Bob can spend on his way. 
The second line contains the integer N, 2 <= N <= 100, the total number of cities. 

The third line contains the integer R, 1 <= R <= 10000, the total number of roads. 

Each of the following R lines describes one road by specifying integers S, D, L and T separated by single blank characters : 
  • S is the source city, 1 <= S <= N 
  • D is the destination city, 1 <= D <= N 
  • L is the road length, 1 <= L <= 100 
  • T is the toll (expressed in the number of coins), 0 <= T <=100

Notice that different roads may have the same source and destination cities.
Output
The first and the only line of the output should contain the total length of the shortest path from the city 1 to the city N whose total toll is less than or equal K coins. 
If such path does not exist, only number -1 should be written to the output. 
Sample Input
5671 2 2 32 4 3 33 4 2 41 3 4 14 6 2 13 5 2 05 4 3 2
Sample Output
11

题意:求最短路并且保证钱够用的情况下,输出最短距离。

有两种方法:用dij+优先队列。或者dfs剪枝。也可以dij+队列+剪枝。

都需要用邻接表 包括 链表和数组模拟(不用邻接表应该是超时的)

方法不难,但是head[110],road[10010].next; 这两个数组是真的难理解(链表的模拟)

重点解释一下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int k,n,r;struct node{    int s,d,l,t,next;}road[10500];int headlist[105];int main(){    while(~scanf("%d%d%d",&k,&n,&r))    {       memset(headlist,-1,sizeof(headlist));       for(int i=0;i<r;i++)       {        scanf("%d%d%d%d",&road[i].s,&road[i].d,&road[i].l,&road[i].t);        road[i].next=headlist[road[i].s];         headlist[road[i].s]=i;printf("road[%d].s=%d  road[%d].next=%d  headlist[%d]=%d\n",i,road[i].s,i,road[i].next,road[i].s,headlist[road[i].s]);      }    }}
这个代码复制下来运行一下,希望你能看懂一点;输入样例

5
6
7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2

给他们每组数据一个编号i=(0到r-1)。

他们就跟排队一样,往下看:

从1出发能到2,3

从2出发能到4

从3出发能到4,5

从4出发能到6

从5出发能到4

如果我问 从1出发能到哪,你肯定知道从1出发能到2,3。

但是程序执行的时候是倒过来的,先去3,再去2,因为head[1]表示的是从1到3的编号i。只要找到了1到3的编号i,

而这个编号i里的road[i].next存的就是1到2的编号。然后你就可以把1能到的地方(倒序)遍历一遍。


(一定记住road[i].next存的是编号,headlist[k]里面存的是编号i,是地点k可以到达的最后一个地方的编号,而根据这个编号i用road[i].next就可以表示上一个编号,然后再找上一个编号,直到到road[i].next==-1,说明已经把地点k能去的地方找完了

memset(headlist,-1,sizeof(headlist));

road[i].next=headlist[road[i].s];
headlist[road[i].s]=i; (headlist[]被用过之后,将再次更新它存的编号

for( i=headlist[x];i!=-1;i=road[i].next)(更新编号i遍历地点x能去的所有地方)

这个代码,你应该能看懂一点吧

看懂了然后就可以看懂下面的代码了

dfs+剪枝+(数组模拟的邻接表) AC代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<map>#include<string>#define LL long long int#define inf 0x3f3f3f3f#define N 100010#define mod 10000007using namespace std;//dfs+剪枝int k,n,r,ans,pt,pv,pf;struct node{    int s,d,l,t,next;}road[10500];int headlist[105];int dis[105];void dfs(int x,int sum,int step){    int i;        if(x==n)        {            ans=step;            return ;        }    for( i=headlist[x];i!=-1;i=road[i].next)    {        pt=road[i].d;        if(dis[pt])continue;        pf=sum+road[i].t;        if(pf>k)continue;        pv=step+road[i].l;        if(pv>=ans)continue;        dis[pt]=1;        dfs(pt,pf,pv);        dis[road[i].d]=0;    }}int main(){    while(~scanf("%d%d%d",&k,&n,&r))    {    ans=inf;    memset(dis,0,sizeof(dis));    memset(headlist,-1,sizeof(headlist));    dis[1]=1;    for(int i=0;i<r;i++)    {        scanf("%d%d%d%d",&road[i].s,&road[i].d,&road[i].l,&road[i].t);        road[i].next=headlist[road[i].s];         headlist[road[i].s]=i;//         printf("road[i].s=%d,&road[i].d=%d,&road[i].l=%d,&road[i].t=%d,road[i].next=%d,headlist[%d]=%d\n",road[i].s,road[i].d,road[i].l,road[i].t,road[i].next,road[i].s,headlist[road[i].s]);    }    dfs(1,0,0);        if(ans==inf)            printf("-1\n");        else            printf("%d\n",ans);    }}


dij+优先队列+(数组模拟的邻接表)AC代码:


#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<string>#define inf 0x3f3f3f3f#define mod 10000007using namespace std;//dij+优先队列struct node  {      int u,v;//起点,终点      int l,t;//长度,花费  }e[10010];  struct qq  {      int x,t,l;//节点下标,当前源点到x的花费,距离      friend bool operator < (qq a,qq b)      {          if(a.l!=b.l) return a.l>b.l;//距离小的优先级高           return a.t>b.t;      }  };  int head[105],next[10010];//head[i]指向顶点i的第一条边,next[i]指向第i条边的下一条边    int main()  {      //freopen("1724.txt","r",stdin);      int k,n,r;      int i,j;      while(scanf("%d%d%d",&k,&n,&r)!=EOF)      {          memset(head,0,sizeof(head));          for(i=1;i<=r;i++)          {              scanf("%d%d%d%d",&e[i].u, &e[i].v, &e[i].l, &e[i].t);              next[i]=head[e[i].u];//逆序存储从u出发的所有边              head[e[i].u]=i;          }          qq u={1,0,0};          priority_queue<qq> q;          q.push(u);//源点去队列          int ans=-1;          int inq[maxn];          memset(inq,0,sizeof(inq));  //同一个u可能是源点从不同种路径到达u.x,路径是随着抛出的顺序递增,但是金钱却不一定,由于金钱的限制,我们只能寻求一条路径相对较短,费用在预算之内的路径.          while(!q.empty())          {              u=q.top(); q.pop();              int x=u.x;              if(x==n) //找到一条到达目标的最短路径              {                  ans=u.l;                  break;              }              for(i=head[x];i;i=next[i])              {                  if(u.t + e[i].t <=k)//在容许的范围                  {                      qq t;                      t.x=e[i].v;                      t.l=e[i].l+u.l;                      t.t=e[i].t+u.t;                      q.push(t);                  }              }          }          printf("%d/n",ans);      }      return 1;  }  



dij+优先队列+<vector>链表,AC代码:(链表道理跟数组模拟是一模一样,看代码应该能看懂的)

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<vector>#include<map>#include<string>#define LL long long int#define inf 0x3f3f3f3f#define N 100010#define mod 10000007zfhusing namespace std;int k,n,r;struct qq{    int x,l,t;    friend bool operator < (qq a,qq b)    {        if(a.l!=b.l) return a.l>b.l;        return a.t>b.t;    }};vector<qq>e[105];int main(){    while(~scanf("%d%d%d",&k,&n,&r))    {        for(int i=1;i<=r;i++)        {            int u,v,l,t;            scanf("%d%d%d%d",&u, &v, &l, &t);            e[u].push_back((qq){v,l,t});//把数据放进去,链表会自己记录(e[k][h]表示地点k(到他能到的)第h的地方,具体还是看代码理解吧)。        }        qq u={1,0,0};        priority_queue<qq>q;        q.push(u);        int ans=-1;        while(!q.empty())        {            u=q.top();            q.pop();            int y=u.x;            if(y==n)            {                ans=u.l;                break;            }            for(int i=0;i<e[y].size();i++)            {                if(u.t+e[y][i].t<=k)                {                    qq tt;                    tt.x=e[y][i].x;                    tt.l=e[y][i].l+u.l;                    tt.t=e[y][i].t+u.t;                    q.push(tt);                }            }        }        printf("%d\n",ans);    }}













0 0