poj 3635 (最短路,广搜,邻接表,dp)

88 篇文章 0 订阅
19 篇文章 0 订阅
        题意 :  n个城市,每个城市的油价不同,m条连通这些城市之间的路,提供油桶的容量,要你求出由城市s出发到城市e的最小费用,其中刚开始时油桶为空,一单位的油可以走以单位的长度。

       看到这题目觉得很难,这主要是不会建图和DP的思想.


       事实上就是类似dijsktra算法的搜索,搜索最短路。

       这个题目的图的点事实上有两个维.

        一个是费用,一个是地点,(比如在位置0有1升油是一个点,在位置0有2升油又是另外一个点)

如果把这种点抽象出来,把费用看过边,那么最少费用就可以按照dijsktra算法那样不断的加入点(至于正确性请看dijkstra算法的证明,贪心性质,非负权边)

        于是得到一个扩展结点的策略:

1.每一次加一升油(因为题目的条件这些都是整数,所以可以类似离散化处理,一点一点加入油)

2.如果加的油足够走到下一个结点,那就走到下一个结点吧(记得减去路上消耗的油,还有花费不变...)




#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define inf 0x7fffffff
const int MAX=1002;
struct EDGE
{
    int v;//终点
    int len;//边的长度
    int next;
}edge[10009];//一定要边的范围


int head[MAX];
int k;
int n,m;
int S,V,E;
int a[1003];
int dp[1002][102];
bool used[1002][102];


void insert(int u,int v,int len)
{
  edge[k].v=v;
  edge[k].len=len;
  edge[k].next=head[u];
  head[u]=k;
  k++;//正边
   edge[k].v=u;
   edge[k].len=len;
   edge[k].next=head[v];
   head[v]=k;
   k++;//反边
}


struct Node
{
    int posion;
    int vale;
    int fule;
    friend bool operator <(Node a,Node b)
    {
       return a.vale>b.vale;
    }
};


priority_queue<Node>my;
Node next,now;


int dij(int s)
{
  while(!my.empty())
  my.pop();
  memset(used,0,sizeof(used));
    for(int i=1;i<=n;i++)
    for(int j=0;j<=100;j++)
        dp[i][j]=inf;


  now.posion=s;
  now.fule=0;
  now.vale=0;
  my.push(now);
  dp[s][0]=0;
  used[s][0]=1;


  while(!my.empty())
  {
      now=my.top();
      my.pop();
      int x=now.posion;
      int y=now.fule;
      int z=now.vale;
      used[x][y]=1;
      if(x==E)
      return z;
      if(y+1<=V&&!used[x][y+1]&&dp[x][y+1]>dp[x][y]+a[x])
      {
        dp[x][y+1]=dp[x][y]+a[x];
        next.fule=y+1;
        next.posion=x;
        next.vale=dp[x][y+1];
        my.push(next);
      }


      for(int i=head[x];i!=-1;i=edge[i].next)
      {
              int t=edge[i].v;
              int kk=y-edge[i].len;
              if(kk>=0&&!used[t][kk]&&z<dp[t][kk])
              {
                dp[t][kk]=z;
                next.posion=t;
                next.fule=kk;
                next.vale=dp[t][kk];
                my.push(next);
              }
      }
  }
  return -1;
}
int ans;
int uu,vv,cc;
int cas;


int main()
{
   int i;
   scanf("%d%d",&n,&m);
   {
    for( i=1;i<=n;i++)
    head[i]=-1;
    k=0;


    for( i=1;i<=n;i++)
      scanf("%d",&a[i]);


    for( i=0;i<m;i++)
    {
        scanf("%d%d%d",&uu,&vv,&cc);
        uu++;vv++;
        insert(uu,vv,cc);
    }
//    for(int x=1;x<=n;x++)
//    for(int i=head[x];i!=-1;i=edge[i].next)
//    {
//        cout<<x<<' '<<edge[i].v<<' '<<edge[i].len<<endl;
//    }
    scanf("%d",&cas);
    while(cas--)
    {
      scanf("%d%d%d",&V,&S,&E);;
      S++;E++;
      ans=dij(S);
      if(ans==-1)
        printf("impossible");
      else
        printf("%d\n",ans);
    }
   }
    return 0;
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值