首页 > 其他分享 >AcWing94场周赛复盘

AcWing94场周赛复盘

时间:2023-03-12 16:44:57浏览次数:35  
标签:周赛 dist int AcWing94 样例 复盘 id 时刻 短路

快速手速题不能犹豫

已知,每个背包最多可以装 件物品。

请你计算,要装下 件物品最少需要多少个背包。

输入格式

一个整数 。

输出格式

一个整数,表示所需背包的最少数量。

数据范围

所有测试点满足 。

输入样例1:

5

输出样例1:

1

输入样例2:

12

输出样例2:

3

代码实现

#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
cin>>n;
cout<<n/5+(n%5!=0)<<endl;

return 0;
}

单源最短路Dijkstra算法复习

Dijkstra算法的证明

现有的点分为两大集合类:即已经确定最短路的点和还未确定最短路的点

因为Dijktra算法只能处理边的权值为非负的情况,因此初始化时,只能确定源点到源点的最短路为0,并加入已经确定最短路的点的集合中,其余所有点都是还未确定最短路的点。

未确定最短路的点的集合特点是,其前驱都是已经确定最短路的点。因此此时在未确定最短路的点集中选择一个距离最小的点 (这个节点是:没有确定最短路径的节点中距离源点最近的点)其距离记为。那么我们就可以说这个 就是源点到当前这个点 的最短距离。

这里证明用反证法:

假设当前这个距离 不是源点到 的最短路径,那么必然有最短路径为

这里注意,源点到当前点的最短路径一定是从源点 出发的指向 的一条路径,听起来好像是个废话,但是这决定了我们可以不断地寻找当前点的前驱,即最后一个前驱一定是源点。

那么如下图所示, 就是最后一个在未确定最短路的点, 的前驱就是一定在已经确定最短路的点集中的点。

堆优化版Dijkstra算法的模板熟练默写

#include <bits/stdc++.h>
using namespace std;
const int N = 150010, INF = 0x3f3f3f3f;
int h[N],ne[N],e[N],w[N],idx;
bool st[N];
int dist[N];
int n,m;
typedef pair<int,int>PII;
#define x first
#define y second
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void dijkstra()
{
priority_queue<PII,vector<PII>,greater<PII> > heap;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
heap.push({0,1});

while (heap.size())
{
auto t = heap.top();
heap.pop();
int id = t.y;
int distance = t.x;
if (st[id]) continue;
st[id]=true;
for (int i=h[id];~i;i=ne[i])
{
int j = e[i];
if (dist[j]>distance+w[i])
{
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
while (m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}

dijkstra();
if (dist[n]==INF) puts("-1");
else printf("%d\n",dist[n]);

return 0;
}

结合周赛题目具体运用

给定一个 个点 条边的不含重边和自环无向图

点的编号为 ,边的编号为 。

在 时刻,你位于 号点。

你的任务是尽可能早的抵达 号点。

第 条边连接点 和点 ,通过此边需要花费的时间为 。

在整个移动过程中,你只有在整数时刻才能离开某一点前往另一点。

此外,第 个点有 个禁止离开时刻。

在每个点的禁止离开时刻,你无法离开该点前往其它点。

请你计算抵达 号点的最早可能时刻。

输入格式

第一行包含两个整数 。

接下来 行,其中第 行包含三个整数 ,表示第 条边连接点 和 ,通过此边需要花费的时间为 。

接下来 行,其中第 行首先包含一个整数 ,表示第 个点有 个禁止离开时刻,随后包含 个互不相同的升序排序的整数 ,表示所有禁止离开时刻。

输出格式

一个整数,表示抵达 号点的最早可能时刻。

如果无法抵达 号点,则输出 -1。

数据范围

前 个测试点满足 。
所有测试点满足 ,,,,,,,所有 相加之和不超过 。

输入样例1:

4 6
1 2 2
1 3 3
1 4 8
2 3 4
2 4 5
3 4 3
0
1 3
2 3 4
0

输出样例1:

7

样例1解释

从点 到点 ,考虑如下三条路线:

  • 在时刻 通过第 条边离开点 ,并于时刻 抵达点 。
  • 在时刻 通过第 条边离开点 ,并于时刻 抵达点 ,由于点 在时刻 禁止离开,所以在时刻 通过第 条边离开点 ,并于时刻 抵达点 。
  • 在时刻 通过第 条边离开点 ,并于时刻 抵达点 ,在时刻 通过第 条边离开点 ,并于时刻 抵达点 。

最佳方案为第三条路线,抵达点 的最早可能时刻为 。

输入样例2:

3 1
1 2 3
0
1 3
0

输出样例2:

-1

  1. 越早到当前点,那么就是越早从这个点离开。那么我们就可以只维护到当前点最短的时刻即可。因为无论这个点是否会有禁止的时刻显示,越早到这个点,总是会比晚到这个点尽早地从此点离开出发到下一个点的。
  2. 其实问题实现了转化,对于当前这个点,只需要检验一下和这个点禁止开始的时刻的关系即可,我们要找的就是大于等于 的且不在当前点的禁止开始的时刻中的值

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
#define x first
#define y second
const int N = 100010, M = 2*N, INF = 0x3f3f3f3f;
int h[N],e[M],ne[M],w[M],idx;
int dist[N];
vector<int>delay[N];
bool st[N];
int n,m;
int add_delay(int id,int distance) // 对于每个id的delay数组,去枚举即可
{
for (auto t:delay[id])
{
if (t==distance) distance++;
else if (t>distance) break;
}

return distance;
}
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra()
{
priority_queue<PII,vector<PII>,greater<PII> >heap;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
heap.push({0,1});

while (heap.size())
{
auto t = heap.top();
heap.pop();
int id = t.y;
if (st[id]) continue;
st[id]=true;

int distance = add_delay(id,t.x);

for (int i=h[id];~i;i=ne[i])
{
int j = e[i];
if (dist[j]>distance+w[i])
{
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);

while (m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
}

for (int i=1;i<=n;i++)
{
int cnt;
scanf("%d",&cnt);
delay[i].resize(cnt);
for (int j=0;j<cnt;j++)
scanf("%d",&delay[i][j]);
}

dijkstra();

if (dist[n]==INF) puts("-1");
else printf("%d\n",dist[n]);

return 0;
}

Floyd多源最短路算法证明复习

floyd算法的第一层循环k,即从i出发,到j,且现在只用到了编号为1~k的节点。

即循环到k时,即可求出现在用1~k号节点,图中任意两点的最短距离。

floyd算法应用为:

  1. 求多源最短路
  2. 求传递闭包
  3. 找最小环(找正权图中,价值和最小的环)
  4. 求恰好经过k条边的最短路,倍增思想

的思想

  • 状态表示:定义一个 表示从i出发,走到j,且用到的节点不超过k的路径
  • 状态属性:求路径中的最小值
  • 状态计算:所有的路径集合分为两大类,一类是不经过节点k,一类是经过节点k。那么不经过节点k的路径为 ;经过节点k的路径为

Floyd模板复习

#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int dist[N][N];
int n,m,k;
int main()
{
cin>>n>>m>>k;
// 没有边的初始化为无穷,自身到自身的距离为0
memset(dist,0x3f,sizeof dist);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i==j)
dist[i][j]=0;

while (m--)
{
int a,b,c;
cin>>a>>b>>c;
dist[a][b]=min(dist[a][b],c);
}

for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);

while (k--)
{
int a,b;
cin>>a>>b;
// 这里不能写成dist[a][b]==0x3f3f3f3f,因为要考虑搭到负权边的存在
if (dist[a][b]>=0x3f3f3f3f/2) puts("impossible");
else cout<<dist[a][b]<<endl;
}

return 0;
}

结合具体题目,对Floyd的k的深层理解

给定一个 个点的加权有向图,点的编号为 。

图中任意两点之间都有两条方向相反的边连接两点。

从点 到点 的边的长度为 。

给定一个 的排列 。

你需要对该图依次进行 个操作。

其中,第 个操作是将点 以及该点的所有入边和出边从图中移除。

在执行每一个操作之前,你还需要进行如下计算:

  • 对于每个当前剩余点对 ,计算从点 到点 的最短路长度。
  • 将这些最短路长度全部相加得到最短路之和。

注意:

  • 剩余点对 :两个还未被移除的点 ()组成的点对。
  • 和 是两个不同点对,需分别计算最短路长度并计入最短路之和。
  • 个操作是按顺序依次执行的,前面的操作会对后面的计算产生影响。

请你输出执行每个操作前计算得到的最短路之和。

输入格式

第一行包含整数 。

接下来 行,每行包含 个整数,其中第 行第 个数为 。

最后一行包含 个整数 。

输出格式

共一行,输出 个整数,其中第 个整数表示执行第 个操作之前,计算得到的最短路之和。

数据范围

前 个测试点满足 。
所有测试点满足 ,,,,保证 是一个 的排列。

输入样例1:

1
0
1

输出样例1:

0

输入样例2:

2
0 5
4 0
1 2

输出样例2:

9 0

输入样例3:

4
0 3 1 1
6 0 400 1
2 4 0 1
1 1 1 0
4 1 2 3

输出样例3:

17 23 404 0

题目分析

我们每次尝试求删除点和这点的入边与出边并不好做,想想就很复杂。但是这时候可以转化一下思想,即从后往前依次添加点,去等价从前往后依次删除点。

即对于给定的待删除的 的排列,如果按正序求解,即先求n个点情况下,所有点对的最短路径之和,再求n-1个点情况下...,那么我们转化之后,就是从后往前先添加这个点,然后在只用节点编号不超过这个点的情况下的点对的最小路径之和。那么我们通过倒序添加点求解,再正序输出结果,就实现了正序删除点的效果。

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int dist[N][N];
int n;
int path[N];
bool st[N];
long long ans[N];
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
cin>>dist[i][j];

for (int i=1;i<=n;i++)
cin>>path[i];

for (int u=n;u>=1;u--)
{
int k=path[u];
st[k]=true;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);

for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (st[i]&&st[j])
ans[u]+=dist[i][j];
}

}

for (int i=1;i<=n;i++)
cout<<ans[i]<<" ";

return 0;
}

标签:周赛,dist,int,AcWing94,样例,复盘,id,时刻,短路
From: https://www.cnblogs.com/sdnu-dfl/p/17208442.html

相关文章

  • 6317.统计美丽子数组的数目-336周赛
    统计美丽子数组的数目给你一个下标从0 开始的整数数组nums 。每次操作中,你可以:选择两个满足 0<=i,j<nums.length 的不同下标 i 和 j 。选择一个非负整数......
  • 复盘-记一次慢SQL导致的雪崩
    一.背景交代某NFT数藏平台于3月11日开启抽奖系统,做了社群推广等市场营销行为,期间市场负责人有联系技术负责人,询问是否需要升级服务等,技术负责人回复先观望;该......
  • 光荣一面-复盘
    人事面试为什么来日本(准备了)小时候动漫,高中日本小说,大学日本游戏,憧憬日本。大学学什么光电信息大学院学什么情报工学喜欢什么类型的游戏(准备了)喜欢rpg,......
  • LeetCode 周赛 335,纯纯手速场!
    本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]提问。大家好,我是小彭。昨晚是LeetCode第335场周赛,你参加了吗?这场周赛整体难度不高,有两道模板题......
  • [第335场周赛]质因数分解,多重背包
    喜提AK以及最佳排名:6307. 递枕头提示简单3相关企业​​n​​ 个人站成一排,按从 ​​1​​ 到 ​​n​​ 编号。最初,排在队首的第一个人拿着一个枕头。每秒钟,拿着枕头......
  • 力扣第335场周赛补题题解
    目录1.递枕头2.二叉树中的第K大层和3.分割数组使乘积互质4.获得分数的方法数1.递枕头classSolution{public:intpassThePillow(intn,inttime){......
  • Acwing 93周赛 C
    异或值(字典树)思路唉,人太笨了,知道用字典树,但想不出过程,知其然而不知其所以然。代码voidinsert(intx){ intp=0; for(inti=30;i>=0;i--) { intu=(x>>i)&......
  • 周赛_ABC291
    C-LRUDInstructions2题面说了这样一句:(includingthestartingandendingpoints)我不以为意捏,认为怎么会错过。结果WA了一发。回头去找别人做的,似乎也只是把我用......
  • 一天约了4个面试,复盘一下面试题和薪资福利
    除了最新的面经分享,还有字节大佬的求职面试答疑,告诉你关键问题是什么?少走弯路。另外本文也汇总了6份大厂面试题:字节、腾讯、小米、腾讯云、滴滴、小米游戏。希望对大家有......
  • LeetCode 周赛 334,在算法的世界里反复横跳
    本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]提问。大家好,我是小彭。今天是LeetCode第334场周赛,你参加了吗?这场周赛考察范围比较基础,整体难度......