【bzoj 2662】冻结(还是分层图+spfa)

http://www.lydsy.com/JudgeOnline/problem.php?id=2662

Description

……
例如,我们熟知的Cirno,她的冰冻魔法当然会有对应的 SpellCard 了。 当然,
更加令人惊讶的是,居然有冻结时间的魔法,Cirno 的冻青蛙比起这些来真是小
巫见大巫了。
这说明之前的世界中有很多魔法少女曾许下控制时间的愿望,比如 Akemi
Homura、Sakuya Izayoi(
题目中竟然出现了晓美焰和十六夜咲夜o( ̄︶ ̄)o)
当然,在本题中我们并不是要来研究历史的,而是研究魔法的应用。

我们考虑最简单的旅行问题吧: 现在这个大陆上有 N 个城市,M 条双向的
道路。城市编号为 1~N,我们在 1 号城市,需要到 N 号城市,怎样才能最快地
到达呢?
这不就是最短路问题吗?我们都知道可以用 Dijkstra、Bellman-Ford、
Floyd-Warshall等算法来解决。
现在,我们一共有 K 张可以使时间变慢 50%的 SpellCard,也就是说,在通
过某条路径时,我们可以选择使用一张卡片,这样,我们通过这一条道路的时间
就可以减少到原先的一半。需要注意的是:
1. 在一条道路上最多只能使用一张 SpellCard。
2. 使用一张SpellCard 只在一条道路上起作用。
3. 你不必使用完所有的 SpellCard。

给定以上的信息,你的任务是:求出在可以使用这不超过 K 张时间减速的
SpellCard 之情形下,从城市1 到城市N最少需要多长时间。

Input

第一行包含三个整数:N、M、K。
接下来 M 行,每行包含三个整数:Ai、Bi、Timei,表示存在一条 Ai与 Bi之
间的双向道路,在不使用 SpellCard 之前提下,通过它需要 Timei的时间。

Output

输出一个整数,表示从1 号城市到 N号城市的最小用时。

Sample Input

4 4 1

1 2 4

4 2 6

1 3 8

3 4 8

Sample Output

7

【样例1 解释】

在不使用 SpellCard 时,最短路为 1à2à4,总时间为 10。现在我们可

以使用 1 次 SpellCard,那么我们将通过 2à4 这条道路的时间减半,此时总

时间为7。

HINT

对于100%的数据:1 ≤ K ≤ N ≤ 50,M ≤ 1000。

1≤ Ai,Bi ≤ N,2 ≤ Timei ≤ 2000。

为保证答案为整数,保证所有的 Timei均为偶数。

所有数据中的无向图保证无自环、重边,且是连通的。

思路:

http://blog.csdn.net/qq_36693514/article/details/78311142(上一个题emmmm)
虽然说是和上一题是一样的,但是调起来仍然是麻烦的很啊,尤其是过了样例仍然WA的情况下简直抓狂w
思路和上一题几乎是一样的emmmm,准确的来说这是上一题代码改了改就交上去A掉了emmmmmm
本题主要是改了下判断条件,因为不需要都用上,而且用上的效果也不是直接免除时间而是使时间减半,因此我们在判断使用卡片次数是否超过给定限制和决定是否合法的时候的具体判断是不一样的

代码如下:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
const int inf = 214748364;
const int sz = 1010;
using namespace std;
struct ed{
    int v,len,nxt;
}e[100<<1];
int fir[sz],cnt,i,j,k,a,b,c;
int n,m,p,dis[55][55],s,t;
bool use[55][55];
inline void rd(int &x){
    x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(f) x*=-1;
}
inline void we(int x){
    if(x<0) putchar('-'),x*=-1;
    if(x/10) we(x/10);
    putchar(x%10+'0');
}
inline void add(int u,int v,int len)
{
    e[++cnt].v=v;
    e[cnt].len=len;
    e[cnt].nxt=fir[u];
    fir[u]=cnt;
}
queue<int>q;
int spfa()
{
    int i,v;
    memset(dis,0x3f,sizeof(dis));
    dis[0][s]=0;
    use[0][s]=1;
    q.push(0),q.push(s);
    while(!q.empty())
    {   
        int ci=q.front();q.pop();
        int u=q.front();q.pop();
        for(i=fir[u];i;i=e[i].nxt)
        {
            v=e[i].v;
            if(dis[ci][v]>dis[ci][u]+e[i].len)
            {
                dis[ci][v]=dis[ci][u]+e[i].len;
                if(!use[ci][v])
                {
                    use[ci][v]=1;
                    q.push(ci),q.push(v);
                }
            }
        }
        if(ci<p)
        for(i=fir[u];i;i=e[i].nxt)
        {
            v=e[i].v;
            if(dis[ci+1][v]>dis[ci][u]+(e[i].len>>1))
            {
                dis[ci+1][v]=dis[ci][u]+(e[i].len>>1);
                if(!use[ci+1][v])
                {
                    use[ci+1][v]=1;
                    q.push(ci+1),q.push(v);
                }
            }
        }
    }
    int ret=inf;
    for(i=0;i<=p;i++)
        ret=min(ret,dis[i][t]);
    return ret;
}
int main()
{
    rd(n),rd(m),rd(p);
    for(i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    s=1,t=n;
    we(spfa());
    return 0;
}

其他思路:

话说还有大佬弄出dp做法,这里补充上吧emmm,参考分析
引自 http://blog.csdn.net/sdfzyhx/article/details/52089708

如果没有魔法卡的话,裸的dijkstra。
有魔法卡,加一维进行dp即可。
dp[i][j]表示到i号点用j张魔法卡的最短路径。
还是在这个数组里面找到未使用的最小值,用它更新答案。注意更新的时候分用和不用魔法卡两种。

#include<cstdio>
#include<cstring>
int dp[55][55],fir[55],ne[2010],to[2010],len[2010],vis[55][55];
void add(int num,int f,int t,int l)
{
    ne[num]=fir[f];
    fir[f]=num;
    to[num]=t;
    len[num]=l;
}
int mn(int x,int y)
{
    return x<y?x:y;
}
int main()
{
    int i,j,k,m,n,p,q,x,y,z,min;
    scanf("%d%d%d",&n,&m,&k);
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(i*2,x,y,z);
        add(i*2+1,y,x,z);
    }
    memset(dp,0x3f,sizeof(dp));
    dp[1][0]=0;
    while (1)
    {
        min=0x3f3f3f3f;
        for (i=1;i<=n;i++)
          for (j=0;j<=k;j++)
            if (!vis[i][j]&&dp[i][j]<min)
            {
                min=dp[i][j];
                x=i;
                y=j;
            }
        if (min==0x3f3f3f3f) break;
        vis[x][y]=1;
        for (i=fir[x];i;i=ne[i])
        {
            dp[to[i]][y]=mn(dp[to[i]][y],dp[x][y]+len[i]);
            dp[to[i]][y+1]=mn(dp[to[i]][y+1],dp[x][y]+len[i]/2);
        }
    }
    min=0x3f3f3f3f;
    for (i=0;i<=k;i++)
      min=mn(min,dp[n][i]);
    printf("%d\n",min);
}
发布了75 篇原创文章 · 获赞 80 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36693514/article/details/78312577