【BZOJ1003】最短路+区间dp

传送门

题意

简单来说就是求1,m的最短路,但是现在一共要走n次,也就是n天每天走一次。在某些天的时间里,有一些点不可以走,然后求走n次的最短路。

做题之前:
wk,这题目啥意思啊,让我读一读啊…
?读完一遍了,啥意思啊。题目没抄全吧。

看了下别人的分析之后,原来是这个意思啊!

遂码…

写完代码之后:
怎么不过样例啊???
遂改。
怎么全爆了啊,遂重码

终于过样例了。

提交代码时:
CE了啊????
换了头文件后。
还是CE啊????
什么?BZOJ不能把结构体写在函数里面???

终于AC。

分析:
如果我们假设第i天到第j天的最短路的权值求好了,
【看电影去了,看完电影再来更新】
【看完了,哥斯拉2真好看,哥斯拉nb(振声)】

继续开始分析:

设第i天到第j天的权值为dp[i][j],那么显然答案就是dp[1][n],通过观察我们可以很容易的发现,这不就是个区间dp嘛!于是我们就像区间dp那样转移一下就好了:

  1. 枚举长度
  2. 枚举起点
  3. 枚举断点

于是,我们的转移方程就是这个样子:

for(int l=2;l<=n;l++)
{
    for(int i=1;i+l-1<=n;i++)
    {
        int j = i+l-1;
        for(int k=i;k<j;k++)
        {
            dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+K);
        }   
    }
}

(很简单吧)

转移方程很显然,我们在第k天变换了路线,那么我们就需要加上K,因为每一次变换我们需要花费K点。(注意大小写)

那么dp[i][j]怎么求呢?

如果第i天到第j天没有什么港口不能使用的话,很显然dp[i][j]就是1到m的最短路了。
那么如果在最短路的港口不能用了咋办呢,我们当它不存在就行了,具体看这里:

for(int i=0;i<G[nw.now].size();i++)
{
    int v = G[nw.now][i].to;
    int w = G[nw.now][i].cost;
    if(!isok(v,s,t)) continue;
    if(dis[v]>dis[nw.now]+w)
    {
        dis[v] = dis[nw.now]+w;
        q.push({v,dis[v]});
    }
} 

这个是更新路径的时候的代码部分,如果第s天到第t天的v不能走的话,我们就当它不存在。

于是dp[i][j]就可以求出来了。

for(int i=1;i<=n;i++)
{
    for(int j=i;j<=n;j++)
    {
        dp[i][j] = dijkstra(i,j);
    }
}

于是我们就把这道题做完了。
还是贴一下代码吧:

/**************************************************************
    Problem: 1003
    User: ACgay_caoyue
    Language: C++
    Result: Accepted
    Time:84 ms
    Memory:1640 kb
****************************************************************/
 
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pb push_back 
using namespace std;
const int maxn = 210;
struct edge
{
    int to;
    int cost;
};
struct node
{
    int now;
    int cost;
    bool operator<(const node &rhs)const
    {
        return cost>rhs.cost;
    }
};
vector<edge> G[maxn];
int ban[maxn][maxn];
int n,m,K,e,d;
int dis[maxn];
int dp[maxn][maxn];
bool isok(int x,int s,int t)
{
    for(int i=s;i<=t;i++)
    {
        if(ban[x][i]) return false;
    }
    return true;
}
int dijkstra(int s,int t)
{
    memset(dis,INF,sizeof(dis));
    dis[1] = 0;
    priority_queue<node> q;
    q.push({1,dis[1]});
    while(!q.empty())
    {
        node nw = q.top();
        q.pop();
        if(dis[nw.now]<nw.cost) continue;
        for(int i=0;i<G[nw.now].size();i++)
        {
            int v = G[nw.now][i].to;
            int w = G[nw.now][i].cost;
            if(!isok(v,s,t)) continue;
            if(dis[v]>dis[nw.now]+w)
            {
                dis[v] = dis[nw.now]+w;
                q.push({v,dis[v]});
            }
        } 
    }
    return dis[m]==INF?INF:dis[m]*(t-s+1);
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&K,&e);
    for(int i=0;i<e;i++)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        G[x].pb({y,w});
        G[y].pb({x,w});
    }
    scanf("%d",&d);
    for(int i=0;i<d;i++)
    {
        int p,x,y;
        scanf("%d%d%d",&p,&x,&y);
        for(int j=x;j<=y;j++) ban[p][j] = 1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            dp[i][j] = dijkstra(i,j);
        }
    }
    for(int l=2;l<=n;l++)
    {
        for(int i=1;i+l-1<=n;i++)
        {
            int j = i+l-1;
            for(int k=i;k<j;k++)
            {
                dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+K);
            }   
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值