【ZJOI2016】旅行者 - 题解

本文解析了ZJOI2016旅行者题目,介绍了一个网格图上的最短路径问题,提供了三种算法解决方案,包括暴力Dijkstra、分治策略和剪枝优化,探讨了不同算法的时间复杂度和得分情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【ZJOI2016】旅行者

题意:

给你一个网格图以及图上的边权,多个询问,求网格图内两点的最短路径。点数不多于 $ 20000 $ ,询问不超过 $ 100000 $ ,边权不超过 $ 10000 $ 。

题解:

算法一:

对于每次询问暴力跑两点最短路,听说是网格图, $ Spfa $ 再见, $ Dijkstra $ 加堆优化飞起。得分 $ 20 $ 。

算法二:

在线做法已经很难再快了,考虑离线。这是一个网格图,所以我们考虑分治,每次处理左上端点为 $ (x1, y1) $ 右下端点为 $ (x2, y2) $ 矩形且询问的点都在范围内的询问。然后我们要将这个矩形切开,当然最好是从中间切开。然后我们考虑当前所有询问经过中线的答案是否比当前答案更优,然后将询问分类并进行分治(跨过中线的两个点答案已经算好,其他的还需递归分治)。我们发现将矩形较长边从中间切开较优。时间复杂度玄学(听说是 $ O(n \sqrt{n} \log{n}) $ 的)。
咦,被卡常了?得分 $ 50 $ ~ $ 100 $ 分。

算法三:

考虑剪枝。如果当前矩形内询问只有一两个时直接暴力查询即可,其实也没快多少

代码:
#include<bits/stdc++.h>
using namespace std;
const int N=20010,M=100010,INF=2e9;
const int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int n,m,k,e[N][4];
struct Q { int l1,r1,l2,r2,id; }; Q q[M],ta[M],tb[M];
int ans[M],dis[N];
struct D {
    int id,w; D(int Id=0,int W=0):id(Id),w(W) {}
    inline bool operator<(const D &yy)const { return w>yy.w; }
};priority_queue <D> h;

int read() {
    register int tmp=0;register char c=getchar();
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';tmp=(tmp<<1)+(tmp<<3)+(c^48),c=getchar());
    return tmp;
}
inline int Min(int x,int y) { return x<y? x:y; }
inline int g(int x,int y) { return (x-1)*m+y; }
void Dijkstra(int l1,int r1,int l2,int r2,int s) {
    for(int i=l1;i<=l2;i++) for(int j=r1;j<=r2;j++) dis[g(i,j)]=INF;
    dis[s]=0,h.push(D(s,0));
    for(;!h.empty();) {
        D u=h.top(); h.pop(); if(u.w!=dis[u.id]) continue;
        int x=(u.id-1)/m+1,y=(u.id-1)%m+1,tx,ty,w;
        for(int i=0;i<4;i++) {
            tx=x+dir[i][0],ty=y+dir[i][1],w=e[u.id][i];
            if(tx<l1||tx>l2||ty<r1||ty>r2) continue;
            if(dis[g(tx,ty)]>u.w+w)
                dis[g(tx,ty)]=u.w+w,h.push(D(g(tx,ty),dis[g(tx,ty)]));
        }
    }
}
void solve(int l1,int r1,int l2,int r2,int l,int r) {
    if(l1>l2||r1>r2||l>r) return ;
    if(l==r) {
        Dijkstra(l1,r1,l2,r2,g(q[l].l1,q[l].r1));
        ans[q[l].id]=Min(dis[g(q[l].l2,q[l].r2)],ans[q[l].id]); return ;
    }
    if(l2-l1<=r2-r1) {
        int mid=(r1+r2)/2;
        for(int i=l1;i<=l2;i++) {
            Dijkstra(l1,r1,l2,r2,g(i,mid));
            for(int j=l;j<=r;j++)
                ans[q[j].id]=Min(ans[q[j].id],
                dis[g(q[j].l1,q[j].r1)]+dis[g(q[j].l2,q[j].r2)]);
        }
        int la=0,lb=0;
        for(int i=l;i<=r;i++) {
            if(q[i].r1<=mid&&q[i].r2<=mid) ta[++la]=q[i];
            if(q[i].r1>mid&&q[i].r2>mid) tb[++lb]=q[i];
        }
        for(int i=1;i<=la;i++) q[i+l-1]=ta[i];
        for(int i=1;i<=lb;i++) q[r-i+1]=tb[i];
        if(r1==r2) return ;
        solve(l1,r1,l2,mid,l,l+la-1),solve(l1,mid+1,l2,r2,r-lb+1,r);
    }
    else {
        int mid=(l1+l2)/2;
        for(int i=r1;i<=r2;i++) {
            Dijkstra(l1,r1,l2,r2,g(mid,i));
            for(int j=l;j<=r;j++)
                ans[q[j].id]=Min(ans[q[j].id],
                dis[g(q[j].l1,q[j].r1)]+dis[g(q[j].l2,q[j].r2)]);
        }
        int la=0,lb=0;
        for(int i=l;i<=r;i++) {
            if(q[i].l1<=mid&&q[i].l2<=mid) ta[++la]=q[i];
            if(q[i].l1>mid&&q[i].l2>mid) tb[++lb]=q[i];
        }
        for(int i=1;i<=la;i++) q[i+l-1]=ta[i];
        for(int i=1;i<=lb;i++) q[r-i+1]=tb[i];
        if(l1==l2) return ;
        solve(l1,r1,mid,r2,l,l+la-1),solve(mid+1,r1,l2,r2,r-lb+1,r);
    }
}
int main() {
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<m;j++) e[g(i,j)][1]=e[g(i,j+1)][3]=read();
    for(int i=1;i<n;i++)
        for(int j=1;j<=m;j++) e[g(i,j)][0]=e[g(i+1,j)][2]=read();
    k=read();
    for(int i=1;i<=k;i++) {
        q[i].id=i,ans[i]=INF;
        q[i].l1=read(),q[i].r1=read(),q[i].l2=read(),q[i].r2=read();
    }
    solve(1,1,n,m,1,k); for(int i=1;i<=k;i++) printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/daniel14311531/p/10322757.html

<think>好的,用户需要PT-1的题解或练习题解析。首先得确定PT-1具体指什么,可能是指某个编程题库的第1题,或者某个教材、竞赛中的题目。常见的PT-1可能出现在PAT(浙江大学程序设计能力考试)或者PTA平台的题目中。需要进一步确认,但用户没有提供更多信息,所以得假设是常见的可能性。 接下来,调用搜索工具查找“PT-1 题解”、“PT-1 练习题解析”等相关内容。搜索结果中发现,PT-1常PAT乙级或甲级考试中的第一题相关,比如A+B类型的问题。例如,PAT乙级1001题是“害死人不偿命的(3n+1)猜想”,这类题目通常需要循环和条件判断。 另外,PTA平台上的题目可能涉及不同的算法,比如PT-1可能考察基础输入输出、简单数学运算或模拟题。例如,PT-1可能要求处理字符串反转、数字各位数求和等基础操作。需要根据不同来源的题解,整理出常见的解题思路和代码示例。 还要注意用户可能需要的答案结构,需要分方法或步骤,避免使用第一人称。可能需要将不同题型的解法归类,比如数学问题、字符串处理、数组操作等,并给出对应的代码示例和公式。例如,对于涉及数论的题目,使用数学公式描述算法步骤,如$n = n / 2$(偶数)和$n = (3n + 1)/2$(奇数),直到n变为1,统计步数。 同时,要确保代码格式正确,用三个反引号包裹,并注明语言类型。比如Python或C++的实现。此外,在最后生成相关问题,需基于PT-1的可能扩展,比如具体题号的解析、不同难度题目的策略,或者常见错误分析。 最后,整合所有信息,确保回答清晰、分步骤,并符合格式要求,避免引用堆砌,每个方法间换行,正确使用数学公式和代码块。相关问题部分要列出三个相关且具体的问题,帮助用户进一步查找所需内容。</think>根据网络搜索结果,PT-1通常指程序设计类考试/练习中的基础题型。以下是针对PT-1常见题型的解析方法及代码实现: **数值计算类题型** 典型例题:实现输入两个整数A和B,输出A+B的结果 ```python a, b = map(int, input().split()) print(a + b) ``` 此类题目重点考察输入输出格式处理,注意特殊数据边界值测试 **条件判断类题型** 常见考法:给定数字n,判断其是否为素数 数学判定条件:若n不能被$2 \leq i \leq \sqrt{n}$范围内的任意整数整除则为素数 ```c++ bool isPrime(int n) { if(n <= 1) return false; for(int i=2; i*i<=n; i++) if(n%i == 0) return false; return true; } ``` **循环控制类题型** 经典案例:3n+1猜想(PAT乙级1001) 处理流程: 1. 输入正整数n 2. 循环执行: - 当n为偶数时:$n = n/2$ - 当n为奇数时:$n = (3n + 1)/2$ 3. 统计操作步数直到n=1 ```python n = int(input()) count = 0 while n != 1: n = n//2 if n%2 ==0 else (3*n+1)//2 count +=1 print(count) ``` **字符串处理类题型** 常见形式:字符串反转/统计特定字符 ASCII码转换公式:$ch_{new} = (ch_{original} + shift) \% 128$ ```java public static String reverseString(String s) { return new StringBuilder(s).reverse().toString(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值