But my words, like silent |

MessageBoxA

园龄:4年10个月粉丝:4关注:0

省选模拟 过路费

前言

这道题正向思考是比较难以想出来的,蕴含了一类解题的思路,同时也可以当作一类板子题记忆。

题面

Link

给定一个有向图,求 st 的最短路径。特殊点在于,对于一条路径, 如果经过的边数小于等于 k,那么该路径总长度为构成该路径的所有边的长度之和;否则为该路径上最长的 k 条边的长度之和。

解法

步骤

设答案为 ans,我们先将 ans 赋值为原图的最短路。然后依次枚举每条边的长度 wx,对于每个 wx,我们将原图上每条边的长度 wi 都减去 wx,如果减去后为负数就设为 0,即 wi=max{wiwx,0},求出此时的最短路,加上 k×wx 后更新 ans

正确性证明

最优路径总边数大于 k 的情况

假设已经知道了最优解,wxw 时生成的图的最短路径上留下的边(我们称边长非零的边为留下的边)的数量一定刚好为 k,最短路径上其他的边边长为 0。此时最短路径长度为 Ei(Eiwx),再加上 k×wx 后刚好等于原图这 k 条边长度之和。

可以发现对于每次枚举到的 wx,计算出的解都不会更优于最优解,可以类似理解为答案是具有凸性的:

  • wx<w 时,由于每条边都相对变长了,显然此时最短路径只会变长不会变短。对于最短路径原先的 k 条边,它们加上 k×wx 后变回原边长;但此时的最短路径相比最优解最短路径可能还多出来一些非零边,因此此时的答案一定是大于等于最优解最短路径长度的。
  • wx>w 时,令此时最短路径上留下的边为 k,由于边权减去的变多了,那么肯定 k<k,假如说我们仍强行按照最优解的那 k 条边统计答案,并且让多出来的 (kk) 条边保留负数而不变为 0,此时 Eik(Eiwx)+k×wx 应该是等于最优解长度的,问题是实际上式子为 Eikmax{(Eiwx),0}+k×wx,即不会保留负数,此时统计出的答案将会大于最优解。如果还是没懂可以试着自己手推几个例子,简明扼要的理解就是由于“减去后若是负数就强制设为 0,减的变少了,但加回来的还是一样”导致了答案反而变大。

另外,为什么只需要枚举每条边的长度,而不是枚举所有长度(1,2,3,,MAXN)

假设有两条边的长度分别为 w1​ 和 w2​(w1<w2​),可以发现如果枚举 wx(w1,w2)​,边权变为 0​ 的边与 wx=w1​ 时一致,并且与上文 wx>w​ 同理,答案只会相等或者更劣。因此没必要考虑两条边长之间的长度,最优的 wx​​ 一定是某一条边的边长,解具有离散性。

因此这样一定会枚举到最优解。

最优路径总边数小于等于 k​ 的情况

这种情况说明原图的最短路即为总边数小于等于 k 的最短路,因此在最开始就用原图最短路更新 ans

代码

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x){
    if(x<0){x=-x;putchar('-');}
    if(x>9) wt(x/10);
    putchar(x%10+'0');
}
typedef long long LL;
typedef pair<LL,int> pli;
const int MAXN=1005,MAXM=2005;
int n,m,k,s,t,ecnt=0,head[MAXN];
LL ans;
struct EDGE{
    int v,w,nxt;
}e[MAXM];
inline void add(int u,int v,int w){
    e[++ecnt].v=v;
    e[ecnt].w=w;
    e[ecnt].nxt=head[u];
    head[u]=ecnt;
}
vector<int>price;
inline LL dijkstra(const int &val){
    static bitset<MAXN>vis;
    static LL dis[MAXN];
    priority_queue<pli,vector<pli>,greater<pli> >pq;
    vis.reset();memset(dis,0x3f,sizeof(dis));
    dis[s]=0;pq.push(make_pair(0,s));
    while(!pq.empty()){
        int u=pq.top().second,v;pq.pop();
        LL w;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].nxt){
            v=e[i].v;
            w=max(e[i].w-val,0);
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                pq.push(make_pair(dis[v],v));
            }
        }
    }
    return dis[t];
}
int main(){
    rd(n);rd(m);rd(k);rd(s);rd(t);
    for(int i=1,x,y,z;i<=m;i++){
        rd(x);rd(y);rd(z);
        add(x,y,z);
        price.push_back(z);
    }
    sort(price.begin(),price.end());
    unique(price.begin(),price.end());
    ans=dijkstra(0);
    for(auto it:price){
        ans=min(ans,dijkstra(it)+1LL*k*it);
    }
    wt(ans);
    return 0;
}

总结

这种题的特征是答案具有凸性,含有“恰好 k 个”类似的描述,并且答案是离散的可以枚举,即可以尝试用本文提到的思路作答。

另外,这种做法与wqs二分有思想上的类似之处,可以互相参考。

本文作者:MessageBoxA

本文链接:https://www.cnblogs.com/SkyNet-PKN/p/18040650

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MessageBoxA  阅读(24)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 evening Corn Wave
  2. 2 Группа крови Кино
  3. 3 The Sound Of Silence Simon & Garfunkel
  4. 4 dB doll YUE.STEVEN
Группа крови - Кино
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.