ICPC Problem K Mario Kart

ICPC Arab Collegiate Programming Contest 2013训练赛
Problem K. Mario Kart
题意概述:
一条路上有n个车站,你有m个金币,金币有各自的费用c和价值v,可以通过金币在车站间移动,每次移动要选择一些金币,总成本不能超过L,其价值和必须刚好等于abs(i-j),问从1号站移动到n号站最少移动多少次.
输入:
测试总量N,站点数目n,金币数目m,限制l。
一下m行为费用c和价值v。

一道经典的背包+dijsktra题目,dijkstra部分完全不需要改变,但是数组存储的过程需要些技巧。
首先定义动规数组dp,记录不同相隔距离间的最小花费。

//开始dp全部为极大值
for(int i=1;i<=m;++i)//遍历金币数
{ 
    for(int j=p[n]-p[1];j>=v[i];--j)//从最大距离到当前可能的最小距离遍历
        dp[j]=min(dp[j],dp[j-v[i]]+c[i]); //动规保存每个距离下的最小花费
}

然后创建map数组,如果dp[p[j]-p[i]]]<=l,说明从i
到j有金币可以支撑其到达并且还在限制l之内,那么将map数组的值设为1,说明一步可以达到,否则的话数组的值为极大值。

void build_map()
{
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
        {
            if(p[i]==p[j])
                mp[i][j]=0;
            else
                mp[i][j]=1e9+7;
        }
    }
    for(int i=1; i<=n; ++i)
    {
        for(int j=i+1; j<=n; ++j)
        {
            if(dp[p[j]-p[i]]<=l)
                mp[i][j]=mp[j][i]=1;
        }
    }
}

最后直接通过最短路径dijsktra求得从1到n的最短距离即可。
开始看到题的时候想到是最短路径了,但是并没有直接给出他们的距离。那么难点就在于如何得出不同站点之间的距离。
所以我们先脱离站点,通过dp求出不同距离对应的最小花费,然后构建数组,两次循环遍历站点,求得dp[不同站点之间的距离]如果<=l,说明可以一步到达,就将数组的值设为1,如果非的话,直接就是初始值极大值。
通过二维数组直接最短路径算法求出1-n的最短花费,如果不满足的话输出-1。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, m, l;
int dp[maxn], dis[maxn], vis[maxn], ma[maxn][maxn];
int p[maxn], c[maxn], v[maxn];
int mod = 1e9 + 7;

void build()
{
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            if(p[i]==p[j])
                ma[i][j]=0;
            else
                ma[i][j]=mod;
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=i+1; j<=n; j++){
            if(dp[p[j]-p[i]]<=l){
                ma[i][j]=1;
                ma[j][i]=1;
            }
        }
    }
}

int dijskstra(int u)
{
    memset(vis, 0, sizeof(vis));
    for(int i=1; i<=n; i++){
        dis[i]=ma[u][i];
    }
    dis[u]=0;
    vis[u]=1;
    for(int i=1; i<n; i++){
        int minn=mod, temp;
        for(int j=1; j<=n; j++){
            if(!vis[j]&&dis[j]<minn){
                minn=dis[j];
                temp=j;
            }
        }
        vis[temp]=1;
        for(int j=1; j<=n; j++){
            if(!vis[j] && dis[j]>ma[temp][j]+dis[temp])
                dis[j]=ma[temp][j]+dis[temp];
        }
    }
    if(dis[n]==mod) return -1;
    return dis[n];
}

int main()
{
    int N;
    scanf("%d",&N);
    while(N--){
        scanf("%d %d %d",&n, &m, &l);
        for(int i=1; i<=n; i++){
            scanf("%d",&p[i]);
        }
        sort(p+1, p+n+1);
        for(int i=1; i<=m; i++){
            scanf("%d %d",&c[i], &v[i]);
        }
        for(int i=1; i<maxn; i++){
            dp[i]=mod;
        }
        dp[0]=0;
        for(int i=1; i<=m; i++){
            for(int j=p[n]-p[1]; j>=v[i]; j--)
                dp[j]=min(dp[j],dp[j-v[i]]+c[i]);
        }
        build();
        printf("%d\n",dijskstra(1));
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值