【题目】点击打开链接,密码acm2016

【题意】n个点m条边的有向图,q次询问c,s,t,表示汽车邮箱容量为c,求从起点s到终点t的最小费用。汽车在每个点可以加任意的油,每个点的油价为a[i]。

【题目分析】优先队列的Dij,每个节点保存还剩下的油量fuel和到当前为止所用的花费,dis[i][j]表示在i点油量还剩下j的费用最小值。注意,每次入队列不要一次性把所有符合的油量全部入队列,比如u->v,距离为w,那么油量区间w~c之间的油量都是可以满足从u走到v的,但不要一次性把w~c全部入队列,这样会超时:应该这样:对于队列中的每个点u先看它当前的油量能否顺着边走到下一个节点v去更新v,如果可以就更新v,然后当前点u的油量还可以增加就把它的油量增加一再入队列。

【补充】还有一种记录三维的状态,dp[i][j][k],分别表示费用,剩余油量,所在城市。最小费用优先。但这里没有必要记录在哪个城市,因为维护是最小值。

【AC代码】

#include <stdio.h>
#include <queue>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 102;
const int maxm = 1002;
struct Edge{
   int u,v,w,next;
}E[maxm];
struct node{
   int id,fuel,cost;
   node(){};
   node(int a,int b,int c):id(a),fuel(b),cost(c){};
   friend bool operator<(const node &x,const node &y)
   {
      return x.cost>y.cost;
   }
};

int n,m,cap,s,e;
int num,head[maxn],dis[maxn][maxn];
int a[maxn];//在某个点加油的花费
void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}
void Add_Edge(int u,int v,int w)
{
    E[num].u=u;
    E[num].v=v;
    E[num].w=w;
    E[num].next=head[u];
    head[u]=num++;
}
void Dijstra(int cap,int s,int e);
int main()
{
    int T,cas=1;
    int u,v,w,q;
//    int cap,s,e;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)scanf("%d",&a[i]);
        init();
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            Add_Edge(u,v,w);
            Add_Edge(v,u,w);
        }
//        Dijstra();
        scanf("%d",&q);
        printf("Case %d:\n",cas++);
        while(q--)
        {
            scanf("%d%d%d",&cap,&s,&e);
            Dijstra(cap,s,e);
        }
    }
    return 0;
}

void Dijstra(int cap,int s,int e)
{
    int u,v;
    node cur;
    memset(dis,INF,sizeof(dis));
    priority_queue<node>que;
    dis[s][0]=0;
    que.push(node(s,0,0));
    while(!que.empty())
    {
        cur=que.top();
        que.pop();
        u=cur.id;
        if(dis[u][cur.fuel]<cur.cost)continue;//在当前点的费用最小值比当前点的花费还要小,就不需要优化
        for(int i=head[u]; ~i; i=E[i].next)
        {
            v = E[i].v;
            if(cur.fuel>=E[i].w&&dis[v][cur.fuel-E[i].w]>dis[u][cur.fuel])
            {
                dis[v][cur.fuel-E[i].w] = dis[u][cur.fuel];
                que.push(node(v,cur.fuel-E[i].w,dis[u][cur.fuel]));
            }
        }
        if(cur.fuel<cap&&dis[u][cur.fuel+1]>dis[u][cur.fuel]+a[u])
        {
            dis[u][cur.fuel+1]=dis[u][cur.fuel]+a[u];
            que.push(node(u,cur.fuel+1,dis[u][cur.fuel]+a[u]));
        }
    }
    int ans=INF;
    for(int i=0; i<=cap; i++)ans=min(dis[e][i],ans);
    if(ans==INF)puts("impossible");
    else
    printf("%d\n",ans);
}