【BZOJ1266】【AHOI2006】上学路线route 题解

题面:传送门


    这道题要求两个答案,首先,我们先看第一个,求出最短时间,那跑一遍最短路不就行了吗?由于 n500 n ≤ 500 ,所以什么方法都能过(除非把floyd写成 n4 n 4 )。

    第二个问题要求删去一些带权无向边,使新图的 1n 1 ⇒ n 的最短路长度大于原图的 1n 1 ⇒ n 的最短路长度,并求出删去的最小边权和。看到“最小”两个字,一般不是动态规划,就是网络流,这道题动态规划显然不可行,那我们往网络流上考虑。由于只有删去最短路上的边才能使最短路长度变大。我们可以先求出哪些边在最短路上。那怎么球呢?令 disa,b d i s a , b ab a ⇒ b 的最短路长度, da,b d a , b ab a ⇒ b 的边的长度,那么对于边 (x,y,d) ( x , y , d ) ,如果有

<script type="math/tex; mode=display" id="MathJax-Element-33"></script>
dis1,x+dx,y+disy,n=dis1,n d i s 1 , x + d x , y + d i s y , n = d i s 1 , n
<script type="math/tex; mode=display" id="MathJax-Element-35"></script>

    那么这条边一定在一条最短路里。我们将所有的这种边求出来,就得到了一个图,这时我们以删去的代价为边的流量,跑一遍最小割不就行了吗?这时可能有人会问,如果对于一条无向边,它的正反两条边都在最短路图里,并且全删了,代价会被多算一遍吗?其实仔细分析一下会发现,一条边是不可能正反两次都出现在最短路里的,假设对于无向边 ab a ⟺ b ab a ⇒ b 出现在了最短路中, ba b ⇒ a 也出现在了最短路中,则

<script type="math/tex; mode=display" id="MathJax-Element-39"></script>
dis1,a+da,b=dis1,b d i s 1 , a + d a , b = d i s 1 , b
dis1,b+da,b=dis1,a d i s 1 , b + d a , b = d i s 1 , a
<script type="math/tex; mode=display" id="MathJax-Element-42"></script>

    那么

<script type="math/tex; mode=display" id="MathJax-Element-43"></script>
dis1,b=dis1,b+2da,b d i s 1 , b = d i s 1 , b + 2 · d a , b
<script type="math/tex; mode=display" id="MathJax-Element-45"></script>

    又

<script type="math/tex; mode=display" id="MathJax-Element-46"></script>
da,b>0 d a , b > 0
<script type="math/tex; mode=display" id="MathJax-Element-48"></script>

    所以不符事实,不存在均出现在最短路中的情况。

    因为有网络流,所以时间复杂度就不在此赘述了。。。

    各位觉得我的分析清楚吗?如果觉得好的话,给个好评撒~~~

    再给个代码理解下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
int n,m;
int p[130000],q[130000],t[130000],c[130000];
int ds[510],dt[510];
vector<pair<int,int> >g[510];
struct edge{
    int to,cap,rev;
};
vector<edge>G[510];
void addedge(int frm,int to,int cap)
{
    G[frm].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){frm,0,G[frm].size()-1});
}
int lev[510],iter[510];
void bfs(int s,int t)
{
    memset(lev,-1,sizeof(lev));
    lev[s]=0;
    int q[510];
    int head=0,tail=1;
    q[1]=s;
    while(head<tail)
    {
        int x=q[++head];
        if(x==t)return;
        for(int i=0;i<G[x].size();i++)
        {
            edge &e=G[x][i];
            if(e.cap && lev[e.to]<0)
            {
                lev[e.to]=lev[x]+1;
                q[++tail]=e.to;
            }
        }
    } 
}
int dfs(int s,int t,int f)
{
    if(s==t)return f;
    for(int &i=iter[s];i<G[s].size();i++)
    {
        edge &e=G[s][i];
        if(e.cap && lev[e.to]==lev[s]+1)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        } 
    }
    return 0;
}
int mxflow(int s,int t)
{
    int res=0;
    for(;;)
    {
        bfs(s,t);
        if(lev[t]<0)return res;
        memset(iter,0,sizeof(iter));
        int f;
        while(f=dfs(s,t,1e9))res+=f;
    }
}
void dijk(int x,int dis[])
{
    priority_queue<pair<int,int> ,vector<pair<int,int> > ,greater<pair<int,int> > >q;
    bool vis[510];
    for(int i=1;i<=n;i++)vis[i]=false;
    for(int i=1;i<=n;i++)dis[i]=1e9;
    dis[x]=0;
    q.push(make_pair(0,x));
    for(int i=1;i<=n;i++)
    {
        while(!q.empty() && (vis[q.top().second] || dis[q.top().second]!=q.top().first))q.pop();
        int x=q.top().second;
        q.pop();
        for(int i=0;i<g[x].size();i++)
        {
            int y=g[x][i].first,w=g[x][i].second;
            if(vis[y])continue;
            if(dis[x]+w<dis[y])
            {
                dis[y]=dis[x]+w;
                q.push(make_pair(dis[y],y));
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",p+i,q+i,t+i,c+i);
        g[p[i]].push_back(make_pair(q[i],t[i]));
        g[q[i]].push_back(make_pair(p[i],t[i]));
    }
    dijk(1,ds);
    dijk(n,dt);
    for(int i=1;i<=m;i++)
    {
        if(ds[p[i]]+t[i]+dt[q[i]]==ds[n])addedge(p[i],q[i],c[i]);
        if(ds[q[i]]+t[i]+dt[p[i]]==ds[n])addedge(q[i],p[i],c[i]); 
    }
    printf("%d\n%d",ds[n],mxflow(1,n));
    return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值