首页 > 其他分享 >洛谷P1462spfa + 二分答案

洛谷P1462spfa + 二分答案

时间:2022-11-01 15:45:12浏览次数:94  
标签:二分 10 洛谷 idx int ll inq leq P1462spfa

第一次接触二分答案的题目最开始是没有思路的看了一个题解,然后强行理解之后开始自己打了一遍,然而结果是只得了30分过了3个点其他全wa,之后是漫长的debug,这里想感慨一句自己debug的速度是真慢。先把题目之间拉过来吧。


通往奥格瑞玛的道路

题目背景

在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。

有一天他醒来后发现自己居然到了联盟的主城暴风城。

在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。

题目描述

在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\)。

城市之间有 \(m\) 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

假设 \(1\) 为暴风城,\(n\) 为奥格瑞玛,而他的血量最多为 \(b\),出发时他的血量是满的。如果他的血量降低至负数,则他就无法到达奥格瑞玛。

歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

输入格式

第一行 \(3\) 个正整数,\(n,m,b\)。分别表示有 \(n\) 个城市,\(m\) 条公路,歪嘴哦的血量为 \(b\)。

接下来有 \(n\) 行,每行 \(1\) 个正整数,\(f_i\)。表示经过城市 \(i\),需要交费 \(f_i\) 元。

再接下来有 \(m\) 行,每行 \(3\) 个正整数,\(a_i,b_i,c_i\)(\(1\leq a_i,b_i\leq n\))。表示城市 \(a_i\) 和城市 \(b_i\) 之间有一条公路,如果从城市 \(a_i\) 到城市 \(b_i\),或者从城市 \(b_i\) 到城市 \(a_i\),会损失 \(c_i\) 的血量。

输出格式

仅一个整数,表示歪嘴哦交费最多的一次的最小值。

如果他无法到达奥格瑞玛,输出 AFK

样例 #1

样例输入 #1

4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3

样例输出 #1

10

提示

对于 \(60\%\) 的数据,满足 \(n\leq 200\),\(m\leq 10^4\),\(b\leq 200\);

对于 \(100\%\) 的数据,满足 \(n\leq 10^4\),\(m\leq 5\times 10^4\),\(b\leq 10^9\);

对于 \(100\%\) 的数据,满足 \(c_i\leq 10^9\),\(f_i\leq 10^9\),可能有两条边连接着相同的城市。

思路:

几个关键点和题目的理解

1、 我们有两个关键的量,一个是血量,另一个是金钱。

2、在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少对于这句话的理解,就是我们先有一条能到达终点的路径,在这个路径上我们找到这条路径上收取费用最多的城市。然后能到终点的路径有多条,所以这个收取费用最多的城市也有多个,我们要找到的就是最小的那一个。

3、我们如果要枚举每一个答案的话时间复杂度就是n,而最短路算法的时间复杂度,dijistkal是nlogn,而spfa出题人不卡的话一般是km, k为常数,如果出题人卡数据的话会退化为mn,暴力枚举的话时间复杂度就是n^2logn或者nm,是过不了的,

4、题目中出现最大值最小,一般考虑二分答案,这是我们看一下答案是否是具有单调性的呢,(目前还没想清楚单调性是怎么回事)

还要说一下自己打代码过程和debug中找到自己的问题,1、数据范围边界设置的不合理因为题目中的数据大于int这时设置正无穷就应该更大.而且需要注意会爆int 2、spfa的模版不熟悉还是有不理解的地方,比如使用队列中的点去进行松弛操作,更新它能更新的所有点这一点自己写的时候却写错了,而且看了好多遍竟然都没发现,最后才找到。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>

using namespace std;

typedef long long ll;
const ll N = 1e5 + 10, M = 1e6 + 10, INF = 0x7fffffff;

//用来跑最短路
bool inq[N], vis[N];
ll a[N], l, r;
ll n, m, hp;
ll dist[N];

//链式前向星
ll h[N], e[N], ne[N], w[N], idx;
void add(ll a, ll b, ll c)
{
   w[idx] = c; e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}

//spfa算法
bool spfa(ll x)
{
   for(ll i = 1; i <= n ; ++ i)
   {
       inq[i] = false;
       if(a[i] <= x)   vis[i] = false;
       else    vis[i] = true;
   }
   for(int i = 2; i <= n; ++ i)    dist[i] = 0x7fffffff;
   queue<ll> q;q.push(1);inq[1] = true;
   while(q.size())
   {
       ll t = q.front();q.pop();
       if(vis[t])  continue;
       inq[t] = false;

       for(ll i = h[t]; i != -1; i = ne[i])
       {
           ll j = e[i];
           if(vis[j])  continue;
           if(dist[j] > dist[t] + w[i])
           {
               dist[j] = dist[t] + w[i];
               if(!inq[j])
               {
                   q.push(j);
                   inq[j] = true;
               }
               
           }
       }
   }
   return dist[n] <= hp;
}
int main()
{
   //文件读入数据
   freopen("1.in.txt","r",stdin);
   //初始化表头
   memset(h, -1, sizeof h);
   scanf("%lld%lld%lld", &n, &m, &hp);
   for(ll i = 1; i <= n; ++ i) {scanf("%lld", &a[i]); r = max(r, a[i]);}
   for(ll i = 1; i <= m; ++ i)
   {
       ll a, b, c;scanf("%lld%lld%lld", &a, &b, & c);
       add(a, b, c); add(b, a, c);
   }
   //当最短路不存在时我们直接输出AFK返回;
   if(!spfa(1000000005))  
   {
       cout << "AFK" << endl;
       return 0;
   }
   //二分答案,找到能跑最短路的最小的答案,大于答案的点是不能走到的点在跑最短路时这些点不能经过
   while(l < r)
   {
       ll mid = (l + r ) >> 1;
       if(spfa(mid))   r = mid;
       else    l = mid + 1;
   }
   cout << l;
   return 0;
}

关于spfa算法

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 1e5 + 10, M = 2 * N;

int n, m;

int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c)
{
    w[idx] = c; e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}

int d[N];
bool inq[N];
void spfa()
{
    queue<int> q;
    memset(d, 0x3f, sizeof d);
    memset(inq, 0, sizeof inq);
    d[1] = 0; q.push(1); inq[1] = 1;
    while(q.size())
    {
        auto t = q.front(); q.pop(); inq[t] = 0;
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(d[j] > d[t] + w[j])
            {
                d[j] = d[t] + w[j];
                if(!inq[j])
                {
                    inq[j] = 1;
                    q.push(j);
                }
            }
        }
    } 
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for(int i = 1; i <= m; ++ i)
    {
        int a, b, c; cin >> a >> b >> c;
        add(a, b, c);
    }
    spfa();
    return 0;
}

标签:二分,10,洛谷,idx,int,ll,inq,leq,P1462spfa
From: https://www.cnblogs.com/cxy8/p/16847643.html

相关文章

  • 洛谷 P5369
    先规定一些东西:若存在多个\(p\)使得\(\sum_{i=1}^{p}{a_i}\)最大,默认最大(即最靠右)的一个\(p\)是它的最大前缀和的位置。\(U\)表示全集。假设\(p\)是最大前缀......
  • 二分查找
    二分查找(折半查找)前提:排好顺序的数据//元素存在返回索引,否则返回-1publicstaticintbinarySearch(int[]arr,intdata){intleft=0;intright=arr.l......
  • 洛谷 P1464 Function(dfs+记忆化搜索)
    https://www.luogu.com.cn/problem/P1464单个返回条件的时候,直接return多个返回条件的时候,采用记忆化搜索思想,边存储边继续往下搜索中间穿插记忆化判断,如果之前有过此......
  • UVA12003 Array Transformer(分块,块内二分)
    我们考虑用分块来水过此题我们先将原序列进行分块,对于某个块\(B\)内的数,我们把它们放进一个数组\(block[B][\]\)里,再对数组进行排序。那么我们就得到了\(\sqrt{n}\)......
  • 【洛谷P7816 】【Stoi2032】以父之名
    在洛谷题解中看到了两种做法。法一:与zjr巨佬说的类似,我们先能观察出这个图的几个性质:若只保留边权为\(1\)的边,那么所有点的度数都是奇数。那么也可以得到\(n\)为偶......
  • 顺序查找和二分法
    顺序查找顺序查找:也叫线性查找,从列表第一个元素开始,顺序进行搜索,直到找到元素或搜索到列表的最后一个元素为止,简单理解就是从头走到尾defliner_search(lst,val):......
  • 洛谷 P1153 点和线
    前置知识(1)求两条线段的交点方法1,运用高中的解析几何知识求.方法2,运用向量点积求.考虑向量叉乘。若\(\veca\times\vecb>0\),那么\(\vecb\)在\(\veca\)......
  • 【XSY3899】切割(思维,模拟二分图匹配)
    题面切割题解考虑这么一个边都和坐标轴平行的不规则图形,经过水平或竖直切割后,如何判断切割后的图形是个矩形。容易发现,如果切出来后的图形没有凹进去的点,它就是一个矩......
  • 【XSY3890】【hdu5263】平衡大师(二分,上下界网络流)
    不妨令\(k=m-k\),那么题目的意思就是至多删去\(k\)条边。首先二分答案\(t\),然后求最少需要删去多少的边,如果最少需要删去的边\(\leqk\)则合法。在原图中统计每一个......
  • 洛谷 P1789【Mc生存】插火把
    萌新写的代码,长但模块化#include<stdio.h>#defineROW100#defineCOLUMN100intmap[ROW][COLUMN];/*函数测试数据={1,1,1,0,0,1,1,0,1,0,1,1,1,0,0,0,0,0,0,0......