给个负责任的OJ:汽车加油


Description

给定一个N*N 的方形网格,设其左上角为起点◎,坐标为(1,1),X 轴向右为正,Y轴向下为正,每个方格边长为1,如图所示。一辆汽车从起点◎出发驶向右下角终点▲,其坐标为(N,N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在
行驶过程中应遵守如下规则:
(1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在起点与终点处不设油库。
(2)汽车经过一条网格边时,若其X 坐标或Y 坐标减小,则应付费用B,否则免付费用。
(3)汽车在行驶过程中遇油库则应加满油并付加油费用A。
(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。
(5)(1)~(4)中的各数N、K、A、B、C均为正整数,且满足约束:2<=N <=100,2 <= K <= 10。
设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。


编程任务:
对于给定的交通网格,计算汽车从起点出发到达终点的一条所付费用最少的行驶路线。

Input

第一行是N,K,A,B,C的值。第二行起是一个N*N 的0-1 方阵,每行N 个值,至N+1 行结束。方阵的第i 行第j 列处的值为1 表示在
网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻两个数以空格分隔。

Output

程序运行结束时,将最小费用输出

Sample Input

9 3 2 3 6
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0

Sample Output

12


这个题,完全不像是网络流的类型啊,然后一看就不会写

学了题解之后,发现图论的很多思路和动态规划也是一样一样的,和搜索也是一样的

在(x,y)二维表示坐标,状态表示不够全面的时候,往往在加一维来表示


所以,本题是一个最短路问题

状态的表示量为(i,j,k),其中(i,j)为坐标,k为当前在该格子中的剩余的油量

然后,根据题中的数据来添加状态转移的边和权值


推荐一篇博客:

http://blog.csdn.net/willinglive/article/details/38148805


这个题,还有1个坑点,在于我们需要对maxn的值进行计算

n最大是100,那么maxn是多少?

算大了无所谓(只要不开得过大,超过题目中所给的最大空间)

算小了会是莫名其妙的wa

有可能的达到的范围是:100*100*100

那么需要1e6的数组来保存每个节点的能到的最小值


代码如下:

用SPFA算法跑的最短路,大概复杂度是O(nlogn),是基本可以AC的


#include<bits/stdc++.h>
using namespace std;

const int maxn=1e6+500;
const int maxe=2e6+500;
const int maxsize=1e6+500;
const int INF=1<<30;

struct Edge{
    int u,w,v,nxt;
}edge[maxe];
int cnt,q[maxsize];
int mp[110][110];
int a,b,c,k,n,s,t;
int C[2]={0,0};
int dx[]={-1,0,1,0};
int dy[]={0,-1,0,1};

int getpoint(int k,int x,int y){
    return k*n*n+x*n+y;
}

int Head[maxn],d[maxn];
bool vis[maxn];

void addedge(int u,int v,int w){
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].nxt=Head[u];
    Head[u]=cnt++;
}

void SPFA(){
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=t;i++) d[i]=INF;
    d[s]=0;
    vis[s]=true;
    int l=0,r=0;
    q[r++]=s;
    while(l<r){
        int u=q[l%maxsize];
        l++;
        vis[u]=0;
        for(int i=Head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].v;
            if (edge[i].w+d[u]<d[v]){
                d[v]=edge[i].w+d[u];
                if (!vis[v]){
                    vis[v]=1;
                    q[r%maxsize]=v;
                    r++;
                }
            }
        }
    }
    return;
}

int main(){
    freopen("input.txt","r",stdin);
    while(scanf("%d%d%d%d%d",&n,&k,&a,&b,&c)!=EOF){
        //cout<<n<<" "<<k<<endl;
        s=getpoint(k,1,1);
        t=getpoint(k,n,n);
        C[0]=c;C[1]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&mp[i][j]);
        memset(Head,-1,sizeof(Head));
        cnt=0;
        for(int l=0;l<=k;l++)
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            int temp=getpoint(l,i,j);
            if (l==0) addedge(temp,getpoint(k,i,j),a+C[mp[i][j]]);
            //走不动了,油量不够了,只能在原地加油
            //原地加油分成两种情况,有油库和没有油库
            else{
                for(int dir=0;dir<4;dir++){
                    int x=i+dx[dir],y=j+dy[dir];
                    if (x>=1&&x<=n&&y>=1&&y<=n){
                        if (dir<=1){
                            if (mp[x][y]) addedge(temp,getpoint(k,x,y),b+a);
                            else addedge(temp,getpoint(l-1,x,y),b);
                            //往左上走,位置变了
                            //有油库我们就可以选择加油
                        }
                        else{
                            if (mp[x][y]) addedge(temp,getpoint(k,x,y),a);
                            else addedge(temp,getpoint(l-1,x,y),0);
                            //往右下走,位置变了
                            //有油库我们就可以选择加油
                        }
                    }
                }
            }
            if (l!=k) addedge(temp,getpoint(k,i,j),a+C[mp[i][j]]);
            //如果油箱不满,也可以加油
        }
        SPFA();
        int ans=INF;
        for(int l=0;l<=k;l++)
            if (d[getpoint(l,n,n)]<ans)
                ans=d[getpoint(l,n,n)];
        cout<<ans<<endl;
    }
    return 0;
}