浅谈基础的图算法——最短路算法相关例题讲解(c++)

news/2024/9/15 3:54:26/ 标签: 算法, c++, 开发语言

例题讲解

P4667 [BalticOI 2011 Day1] Switch the Lamp On(01最短路)

题面翻译
题目描述

Casper 正在设计电路。有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会。有 N × M N\times M N×M 个这样的元件,你想将其排列成 N N N 行,每行 M M M 个。 电源连接到板的左上角。灯连接到板的右下角。只有在电源和灯之间有一条电线连接的情况下,灯才会亮着。为了打开灯,任何数量的电路元件都可以转动 90°(两个方向)。

在上面的图片中,灯是关着的。如果右边的第二列的任何一个电路元件被旋转 90°,电源和灯都会连接,灯被打开。现在请你编写一个程序,求出最小需要多少旋转多少电路元件。

输入输出格式
输入格式

输入的第一行包含两个整数 N N N M M M,表示盘子的尺寸。 在以下 N N N 行中,每一行有 M M M 个符号 \/,表示连接对应电路元件对角线的导线的方向。

输出格式:

如果可以打开灯,那么输出只包含一个整数,表示最少转动电路元件的数量。

如果不可能打开灯,输出 NO SOLUTION

题目描述

Casper is designing an electronic circuit on a N × M N \times M N×M rectangular grid plate. There are N × M N \times M N×M square tiles that are aligned to the grid on the plate. Two (out of four) opposite corners of each tile are connected by a wire.

A power source is connected to the top left corner of the plate. A lamp is connected to the bottom right corner of the plate. The lamp is on only if there is a path of wires connecting power source to lamp. In order to switch the lamp on, any number of tiles can be turned by 90° (in both directions).

In the picture above the lamp is off. If any one of the tiles in the second column from the right is turned by 90° , power source and lamp get connected, and the lamp is on.

Write a program to find out the minimal number of tiles that have to be turned by 90° to switch the lamp on.

输入格式

The first line of input contains two integer numbers N N N and M M M, the dimensions of the plate. In each of the following N N N lines there are M M M symbols – either \ or / – which indicate the direction of the wire connecting the opposite vertices of the corresponding tile.

输出格式

There must be exactly one line of output. If it is possible to switch the lamp on, this line must contain only one integer number: the minimal number of tiles that have to be turned to switch on the lamp. If it is not possible, output the string: NO SOLUTION

样例 #1
样例输入 #1
3 5
\\/\\
\\///
/\\\\
样例输出 #1
1
提示

对于 40 % 40\% 40% 的数据, 1 ≤ N ≤ 4 1 \le N \le 4 1N4 1 ≤ M ≤ 5 1 \le M \le 5 1M5

对于所有数据, 1 ≤ N , M ≤ 500 1 \le N,M \le 500 1N,M500

思路

每个格子看作点。
如果当前电线是左上到右下:
左上到右下建边权为 0 的边。
右上到左下建边权为 1 的边表示需要改变。
反之亦然。
01 最短路,用队列维护。

AC代码
#include<iostream>
#include<deque> 
#include<cstring>
using namespace std;
const int dx[4]={1,-1,-1,1},dy[4]={1,1,-1,-1},ix[4]={0,-1,-1,0},iy[4]={0,0,-1,-1};
const char a[5]="\\/\\/";
deque <int> x,y;
char map[505][505]; 
int vis [505][505];
int l,c;  
void bfs(){ memset(vis,0x3f,sizeof(vis)); x.push_back(0); y.push_back(0);vis[0][0]=0; while(!x.empty()){ int xx=x.front(); int yy=y.front();x.pop_front(); y.pop_front();for(int i=0;i<4;i++){  int dnx=xx+dx[i]; int dny=yy+dy[i];int inx=xx+ix[i];int iny=yy+iy[i];if(dnx>=0&&dnx<=l&&dny>=0&&dny<=c){  if(a[i]!=map[inx][iny]){ int t=vis[xx][yy]+1;  if(t<vis[dnx][dny]){x.push_back(dnx); y.push_back(dny);vis[dnx][dny]=t; 	}}	else{int t=vis[xx][yy]; if(t<vis[dnx][dny]){ x.push_front(dnx);y.push_front(dny);vis[dnx][dny]=t;	}}}}}cout<<vis[l][c]<<endl; 
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>l>>c; for(int i=0;i<l;i++)cin>>map[i];if((l+c)%2) cout<<"NO SOLUTION\n";else bfs(); return 0;		
} 

P1462 通往奥格瑞玛的道路

题目背景

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

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

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

题目描述

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

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

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

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

歪嘴哦不希望花很多钱,他想知道,在所有可以到达奥格瑞玛的道路中,对于每条道路所经过的城市收费的最大值,最小值为多少。

输入格式

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

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

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

输出格式

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

如果他无法到达奥格瑞玛,输出 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 % 60\% 60% 的数据,满足 n ≤ 200 n\leq 200 n200 m ≤ 1 0 4 m\leq 10^4 m104 b ≤ 200 b\leq 200 b200

对于 100 % 100\% 100% 的数据,满足 1 ≤ n ≤ 1 0 4 1\leq n\leq 10^4 1n104 1 ≤ m ≤ 5 × 1 0 4 1\leq m\leq 5\times 10^4 1m5×104 1 ≤ b ≤ 1 0 9 1\leq b\leq 10^9 1b109

对于 100 % 100\% 100% 的数据,满足 1 ≤ c i ≤ 1 0 9 1\leq c_i\leq 10^9 1ci109 0 ≤ f i ≤ 1 0 9 0\leq f_i\leq 10^9 0fi109,可能有两条边连接着相同的城市。

思路

最大值最小?二分!
保留二分值以下的边,跑最短路,检查是否能活着到达 n 号点

满分代码
#include<bits/stdc++.h>
using namespace std;
const int N=10005,M=100005,MAXN=1000000005;
int f[N],dis[N],h[N];
bool ed[N];
int n,m,b,t;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >qu;
struct edge{int to,ne,w;
}e[M];
void add(int x,int y,int z){e[++t].w=z;e[t].to=y;e[t].ne=h[x];h[x]=t;
}
bool work(int x){if(x<f[1])return 0;memset(dis,0x3f3f3f3f,sizeof(dis));memset(ed,0,sizeof(ed));dis[1]=0;qu.push({0,1});int s1,s2;while(qu.size()){s1=qu.top().second;qu.pop();if(ed[s1])continue;ed[s1]=1;s2=h[s1];while(s2){if(f[e[s2].to]<=x&&ed[e[s2].to]==0&&dis[s1]+e[s2].w<dis[e[s2].to]){dis[e[s2].to]=dis[s1]+e[s2].w;qu.push({dis[e[s2].to],e[s2].to});}s2=e[s2].ne;}}if(dis[n]<b)return 1;return 0;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m>>b;for(int i=1;i<=n;i++)cin>>f[i];int a1,a2,a3;for(int i=1;i<=m;i++){cin>>a1>>a2>>a3;add(a1,a2,a3);add(a2,a1,a3);}if(work(MAXN)==0){cout<<"AFK"<<endl;return 0;}int l=1,r=MAXN,mid=(l+r)>>1;int c;while(l<=r){c=work(mid);if(c)r=mid-1;elsel=mid+1;mid=(l+r)>>1;}cout<<l<<endl;return 0;
}

P1119 灾后重建(Floyd 理解)

题目背景

B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。

题目描述

给出 B 地区的村庄数 N N N,村庄编号从 0 0 0 N − 1 N-1 N1,和所有 M M M 条公路的长度,公路是双向的。并给出第 i i i 个村庄重建完成的时间 t i t_i ti,你可以认为是同时开始重建并在第 t i t_i ti 天重建完成,并且在当天即可通车。若 t i t_i ti 0 0 0 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 Q Q Q 个询问 ( x , y , t ) (x,y,t) (x,y,t),对于每个询问你要回答在第 t t t 天,从村庄 x x x 到村庄 y y y 的最短路径长度为多少。如果无法找到从 x x x 村庄到 y y y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x x x 或村庄 y y y 在第 t t t 天仍未重建完成,则需要输出 − 1 -1 1

输入格式

第一行包含两个正整数 N , M N,M N,M,表示了村庄的数目与公路的数量。

第二行包含 N N N 个非负整数 t 0 , t 1 , ⋯ , t N − 1 t_0,t_1,\cdots,t_{N-1} t0,t1,,tN1,表示了每个村庄重建完成的时间,数据保证了 t 0 ≤ t 1 ≤ ⋯ ≤ t N − 1 t_0 \le t_1 \le \cdots \le t_{N-1} t0t1tN1

接下来 M M M 行,每行 3 3 3 个非负整数 i , j , w i,j,w i,j,w w w w 不超过 10000 10000 10000,表示了有一条连接村庄 i i i 与村庄 j j j 的道路,长度为 w w w,保证 i ≠ j i\neq j i=j,且对于任意一对村庄只会存在一条道路。

接下来一行也就是 M + 3 M+3 M+3 行包含一个正整数 Q Q Q,表示 Q Q Q 个询问。

接下来 Q Q Q 行,每行 3 3 3 个非负整数 x , y , t x,y,t x,y,t,询问在第 t t t 天,从村庄 x x x 到村庄 y y y 的最短路径长度为多少,数据保证了 t t t 是不下降的。

输出格式

Q Q Q 行,对每一个询问 ( x , y , t ) (x,y,t) (x,y,t) 输出对应的答案,即在第 t t t 天,从村庄 x x x 到村庄 y y y 的最短路径长度为多少。如果在第 t t t 天无法找到从 x x x 村庄到 y y y 村庄的路径,经过若干个已重建完成的村庄,或者村庄 x x x 或村庄 y y y 在第 t t t 天仍未修复完成,则输出 − 1 -1 1

样例 #1
样例输入 #1
4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4
样例输出 #1
-1
-1
5
4
提示
  • 对于 30 % 30\% 30% 的数据,有 N ≤ 50 N\le 50 N50
  • 对于 30 % 30\% 30% 的数据,有 t i = 0 t_i=0 ti=0,其中有 20 % 20\% 20% 的数据有 t i = 0 t_i=0 ti=0 N > 50 N>50 N>50
  • 对于 50 % 50\% 50% 的数据,有 Q ≤ 100 Q\le 100 Q100
  • 对于 100 % 100\% 100% 的数据,有 1 ≤ N ≤ 200 1\le N\le 200 1N200 0 ≤ M ≤ N × ( N − 1 ) 2 0\le M\le \dfrac{N\times(N-1)}{2} 0M2N×(N1) 1 ≤ Q ≤ 50000 1\le Q\le 50000 1Q50000,所有输入数据涉及整数均不超过 1 0 5 10^5 105
思路

考虑 Floyd 的算法意义 – 只经过前 k 个点的最短路径,将村庄按重建时间排序,离线下
来在对应的位置直接询问即可。

AC代码
#include<bits/stdc++.h>
#define N 205
using namespace std;
int n,m;
int a[N];
int f[N][N];
inline void updata(int k){for(int i=0;i<n;i++)for(int j=0;j<n;j++)if(f[i][j]>f[i][k]+f[j][k])f[i][j]=f[j][i]=f[i][k]+f[j][k];return;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=0;i<n;i++)cin>>a[i];memset(f,0x3f3f3f3f,sizeof(f));for(int i=0;i<n;i++)f[i][i]=0;int s1,s2,s3;for(int i=1;i<=m;i++){cin>>s1>>s2>>s3;f[s1][s2]=f[s2][s1]=s3; }int q;cin>>q;int now=0;for(int i=1;i<=q;i++){cin>>s1>>s2>>s3;while(a[now]<=s3&&now<n){updata(now);now++;}if(a[s1]>s3||a[s2]>s3)cout<<-1<<endl;else {if(f[s1][s2]==0x3f3f3f3f)cout<<-1<<endl;else cout<<f[s1][s2]<<endl;}}return 0;
} 

P4822 [BJWC2012] 冻结(分层图)

题目背景

“我要成为魔法少女!”

“那么,以灵魂为代价,你希望得到什么?”

“我要将有关魔法和奇迹的一切,封印于卡片之中„„”

在这个愿望被实现以后的世界里,人们享受着魔法卡片(SpellCard,又名符卡)带来的便捷。

现在,不需要立下契约也可以使用魔法了!你还不来试一试?

比如,我们在魔法百科全书(Encyclopedia of Spells)里用“freeze”作为关键字来查询,会有很多有趣的结果。

例如,我们熟知的 Cirno,她的冰冻魔法当然会有对应的 SpellCard 了。当然,更加令人惊讶的是,居然有冻结时间的魔法,Cirno 的冻青蛙比起这些来真是小巫见大巫了。

这说明之前的世界中有很多魔法少女曾许下控制时间的愿望,比如 Akemi Homura、Sakuya Izayoi、……

当然,在本题中我们并不是要来研究历史的,而是研究魔法的应用。

题目描述

我们考虑最简单的旅行问题吧: 现在这个大陆上有 N N N 个城市, M M M 条双向的道路。城市编号为 1 1 1 ~ N N N,我们在 1 1 1 号城市,需要到 N N N 号城市,怎样才能最快地到达呢?

这不就是最短路问题吗?我们都知道可以用 Dijkstra、Bellman-Ford、Floyd-Warshall等算法来解决。

现在,我们一共有 K K K 张可以使时间变慢 50%的 SpellCard,也就是说,在通过某条路径时,我们可以选择使用一张卡片,这样,我们通过这一条道路的时间 就可以减少到原先的一半。需要注意的是:

  1. 在一条道路上最多只能使用一张 SpellCard。
  2. 使用一张SpellCard 只在一条道路上起作用。
  3. 你不必使用完所有的 SpellCard。

给定以上的信息,你的任务是:求出在可以使用这不超过 K K K 张时间减速的 SpellCard 之情形下,从城市 1 1 1 到城市 N N N 最少需要多长时间。

输入格式

第一行包含三个整数: N N N M M M K K K

接下来 M M M 行,每行包含三个整数: A i A_i Ai B i B_i Bi T i m e i Time_i Timei,表示存在一条 A i A_i Ai B i B_i Bi 之间的双向道路,在不使用 SpellCard 之前提下,通过它需要 T i m e i Time_i Timei 的时间。

输出格式

输出一个整数,表示从 1 1 1 号城市到 N N N 号城市的最小用时。

样例 #1
样例输入 #1
4 4 1 
1 2 4 
4 2 6 
1 3 8 
3 4 8
样例输出 #1
7
提示
样例 1 解释

在不使用 SpellCard 时,最短路为 1 → 2 → 4 1 \to 2 \to 4 124,总时间为 10。现在我们可以使用 1 次 SpellCard,那么我们将通过 2 → 4 2 \to 4 24 这条道路的时间减半,此时总时间为7。

数据规模与约定

对于 100 % 100\% 100% 的数据,保证:

  • 1 ≤ K ≤ N ≤ 50 1 \leq K \leq N \leq 50 1KN50 M ≤ 1 0 3 M \leq 10^3 M103
  • 1 ≤ A i , B i ≤ N 1 \leq A_i,B_i \leq N 1Ai,BiN 2 ≤ T i m e i ≤ 2 × 1 0 3 2 \leq Time_i \leq 2 \times 10^3 2Timei2×103
  • 为保证答案为整数,保证所有的 T i m e i Time_i Timei 均为偶数。
  • 所有数据中的无向图保证无自环、重边,且是连通的。
思路

分层图,建 k 层点跑最短路。
另一视角:动态规划

AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=10000000,INF=0x3f3f3f3f; 
struct node{int to,ne,w;
}e[N*2];
int n,m,k,x,y,z,idx,dis[N],vis[N],h[N];
int point(int x,int y){return y*n+x;}
int min(int a,int b){return a<b?a:b;}
void add(int u,int v,int w){e[++idx].ne=h[u],e[idx].to=v,e[idx].w=w,h[u]=idx;}
int read(){int x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}
queue<int> q;
void spfa(){for(int i=1;i<=point(n,k);i++) dis[i]=INF;dis[1]=0;vis[1]=1;q.push(1);while(!q.empty()){int u=q.front();q.pop();for(int i=h[u];i>0;i=e[i].ne){int v=e[i].to;if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;if(!vis[v]){q.push(v);vis[v]=1;}}}vis[u]=0;}
}
int main(){n=read();m=read();k=read();for(int i=1;i<=m;i++){int x=read(),y=read(),z=read();for(int j=0;j<=k;j++) add(point(x,j),point(y,j),z),add(point(y,j),point(x,j),z);for(int j=0;j<k;j++) add(point(x,j),point(y,j+1),z/2),add(point(y,j),point(x,j+1),z/2);}spfa();int ans=INF;for(int i=0;i<=k;i++) ans=min(ans,dis[point(n,i)]);printf("%d",ans);return 0;
}

[USACO07DEC] Sightseeing Cows G

题面翻译

给你一张 n n n m m m 边的有向图,第 i i i 个点点权为 F i F_i Fi,第 i i i 条边边权为 T i T_i Ti

找一个环,设环上的点组成的集合为 S S S,环的边组成的集合为 E E E,最大化 ∑ u ∈ S F u ∑ e ∈ E T e \dfrac{\sum_{u\in S}F_u}{\sum_{e\in E}T_e} eETeuSFu

数据范围: 1 ≤ n , F i , T i ≤ 1 0 3 1\leq n,F_i,T_i\leq 10^3 1n,Fi,Ti103 1 ≤ m ≤ 5 × 1 0 3 1\leq m\leq 5\times10^3 1m5×103

题目描述

Farmer John has decided to reward his cows for their hard work by taking them on a tour of the big city! The cows must decide how best to spend their free time.

Fortunately, they have a detailed city map showing the L (2 ≤ L ≤ 1000) major landmarks (conveniently numbered 1… L) and the P (2 ≤ P ≤ 5000) unidirectional cow paths that join them. Farmer John will drive the cows to a starting landmark of their choice, from which they will walk along the cow paths to a series of other landmarks, ending back at their starting landmark where Farmer John will pick them up and take them back to the farm. Because space in the city is at a premium, the cow paths are very narrow and so travel along each cow path is only allowed in one fixed direction.

While the cows may spend as much time as they like in the city, they do tend to get bored easily. Visiting each new landmark is fun, but walking between them takes time. The cows know the exact fun values Fi (1 ≤ Fi ≤ 1000) for each landmark i.

The cows also know about the cowpaths. Cowpath i connects landmark L1i to L2i (in the direction L1i -> L2i ) and requires time Ti (1 ≤ Ti ≤ 1000) to traverse.

In order to have the best possible day off, the cows want to maximize the average fun value per unit time of their trip. Of course, the landmarks are only fun the first time they are visited; the cows may pass through the landmark more than once, but they do not perceive its fun value again. Furthermore, Farmer John is making the cows visit at least two landmarks, so that they get some exercise during their day off.

Help the cows find the maximum fun value per unit time that they can achieve.

输入格式

* Line 1: Two space-separated integers: L and P

* Lines 2…L+1: Line i+1 contains a single one integer: Fi

* Lines L+2…L+P+1: Line L+i+1 describes cow path i with three space-separated integers: L1i , L2i , and Ti

输出格式

* Line 1: A single number given to two decimal places (do not perform explicit rounding), the maximum possible average fun per unit time, or 0 if the cows cannot plan any trip at all in accordance with the above rules.

样例 #1
样例输入 #1
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
样例输出 #1
6.00
思路

二分答案, ∑ F / ∑ T ≥ k , ∑ F − k T ≥ 0 \sum F/\sum T \geq k,\sum F- kT \geq 0 F/Tk,FkT0
那么问题即为判断是否存在一个正环,spfa 跑最长路即可(取反判负环也可)。

AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+7,M = 5e4+7;
const double eps = 1e-6;
int f[N],h[N],ver[M],ne[M],idx,n,m,a[N],cnt[N];
double e[M],d[N]; 
bool vis[N];
void add(int x,int y,double z){ver[++idx] = y;ne[idx] = h[x];e[idx] = z;h[x] = idx;
}
bool spfa(){queue<int>q;for(int i = 1;i <= n;i++){q.push(i);d[i] = 0;vis[i] = 1;}memset(cnt,0,sizeof cnt);while(q.size()){int x = q.front();q.pop();vis[x] = 0;for(int i = h[x];i;i = ne[i]){int y = ver[i];if(d[y] > d[x] + e[i]){d[y] = d[x] + e[i];if((cnt[y] = cnt[x] + 1 ) > n)return 1;if(!vis[y]){q.push(y);vis[y] = 1;}}}}return 0;
}
int x[N],y[N],z[N];
bool check(double w){idx = 0;memset(h,0,sizeof h);for(int i = 1;i <= m;i++)add(x[i],y[i],w * z[i] - f[x[i]]);return spfa();
}int main(){scanf("%d%d",&n,&m);for(int i = 1;i <= n;i++)scanf("%d",&f[i]);for(int i = 1;i <= m;i++)scanf("%d %d %d",&x[i],&y[i],&z[i]);double l = 0,r = 1000;while(r - l > eps){double mid = (l + r) / 2;if(check(mid))l = mid;else r = mid;}printf("%.2f",l);return 0;
}

[AHOI2014/JSOI2014] 骑士游戏

题目背景

长期的宅男生活中,JYY 又挖掘出了一款 RPG 游戏。在这个游戏中 JYY 会扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽。

题目描述

在这个游戏中,JYY 一共有两种攻击方式,一种是普通攻击,一种是法术攻击。两种攻击方式都会消耗 JYY 一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统 bug,并不保证这一点)。

游戏世界中一共有 N N N 种不同的怪兽,分别由 1 1 1 N N N 编号,现在 1 1 1 号怪兽入侵村庄了,JYY 想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?

输入格式

第一行包含一个整数 N N N

接下来 N N N 行,每行描述一个怪兽的信息;

其中第 i i i 行包含若干个整数,前三个整数为 S i S_i Si K i K_i Ki R i R_i Ri,表示对于 i i i 号怪兽,普通攻击需要消耗 S i S_i Si 的体力,法术攻击需要消耗 K i K_i Ki 的体力,同时 i i i 号怪兽死亡后会产生 R i R_i Ri 个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。

输出格式

输出一行一个整数,表示最少需要的体力值。

样例 #1
样例输入 #1
4
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2
样例输出 #1
26
提示

首先用消耗 4 4 4 点体力用普通攻击,然后出现的怪兽编号是 2 2 2 2 2 2 3 3 3。花费 10 10 10 点体力用法术攻击杀死两个编号为 2 2 2 的怪兽。剩下 3 3 3 号怪兽花费 1 1 1 点体力进行普通攻击。此时村庄里的怪兽编号是 2 2 2 4 4 4。最后花费 11 11 11 点体力用法术攻击将这两只怪兽彻底杀死。一共花费的体力是 4 + 5 + 5 + 1 + 5 + 6 = 26 4+5+5+1+5+6=26 4+5+5+1+5+6=26

对于所有数据 2 ≤ N ≤ 2 × 1 0 5 2 \le N \le 2 \times 10^5 2N2×105 1 ≤ R i , ∑ R i ≤ 1 0 6 1 \le R_i,\sum R_i \le 10^6 1Ri,Ri106 1 ≤ K i , S i ≤ 5 × 1 0 14 1 \le K_i,S_i \le 5 \times 10^{14} 1Ki,Si5×1014

思路

f [ x ] f[x] f[x]表示杀死 x 的最小代价,那么有 f [ x ] = m i n ( S x + ∑ f [ y ] , K i ) f[x]=min(S_x+\sum f[y],K_i) f[x]=min(Sx+f[y],Ki)
显然这个转移会有环,并且是一个取 min 的转移,那么类比最短路,类比 dijistra,刚开
始令 f [ x ] = K i f[x]=K_i f[x]=Ki,并扔到堆里,每次选出最小的并更新其他人的 dp 值。
理解:大的 dp 值一定由小的 dp 来转移,那么从最小的 dp 值开始,则无后效性。

AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read() {char cc = getchar(); ll cn = 0, flus = 1;while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();return cn * flus;
}
const ll N = 2e5 + 5 ,M = 1e6 + 5 ; 
ll n, s[N], k[N], dp[N], vis[N], ans[N], r[N] ;
vector<int> mp[N] ; 
struct node {ll id, w ; bool operator < (const node& x) const {return w > x.w ; }
};
priority_queue<node> q ; 
int main()
{n = read() ; ll x, siz ; for(ll i=1;i<=n;i++)	{s[i] = read(), k[i] = read(), r[i] = read() ;for(ll j= 1; j<=r[i];j++) x = read(), mp[x].push_back(i) ; q.push((node){i,k[i]}), dp[i]=s[i] ;}while( !q.empty() ) {ll u = q.top().id, w = q.top().w ; q.pop() ; if( vis[u] ) continue ; vis[u] = 1, ans[u] = w, siz = mp[u].size() - 1 ; for(ll i=0;i<=siz;i++) {ll v = mp[u][i] ; if(vis[v]||dp[v]>k[v]) continue ; r[v]--;dp[v]+=w ;if(r[v]==0)q.push((node){v,dp[v]}) ;}}printf("%lld\n",ans[1]) ;return 0;
}

P9402 [POI2020-2021R3] Droga do domu(分层图)

题目背景

译自 XXVIII Olimpiada Informatyczna - III etap Droga do domu。

d1t1。

题目描述

n n n 个点, m m m 条边,无重边自环,边有长度。

1 1 1 号点是学校, n n n 号点是家。

s s s 条公交线路。公交逢点必停,且一个点不会停两次。在一条边上行驶的时间就是它的长度。给定了第一班公交发车时间和发车间隔。

在时刻 t t t 从学校出发,至多换乘 k k k 次,求最早什么时候到家。

只计算路上时间和等车时间。换乘时间不计。

输入格式

第一行:五个整数 n , m , s , k , t n,m,s,k,t n,m,s,k,t

接下来 m m m 行:每行三个整数 a , b , c a,b,c a,b,c,表示有一条边连接 a , b a,b a,b,长度为 c c c

接下来 2 s 2s 2s 行:每两行描述一条公交线路:

  • 第一行三个整数 l , x , y l,x,y l,x,y,表示它共停靠 l l l 个点,第一班在时刻 x x x 发车,每两班之间时间间隔为 y y y
  • 第二行 l l l 个整数 v 1 , … , v l v_1,\dots,v_l v1,,vl,依次为它停靠的 l l l 个点。
输出格式

一行一个整数,答案。

如果不能到家,那么输出一行一个字符串 NIE

样例 #1
样例输入 #1
4 4 2 1 1
1 2 2
2 3 4
1 3 3
4 3 2
4 0 10
1 2 3 4
3 2 7
1 3 2
样例输出 #1
8
样例 #2
样例输入 #2
10 45 17 10 123
1 2 1
1 3 100
1 4 100
1 5 100
1 6 100
1 7 100
1 8 100
1 9 100
1 10 100
2 3 1
2 4 100
2 5 100
2 6 100
2 7 100
2 8 100
2 9 100
2 10 100
3 4 1
3 5 100
3 6 100
3 7 100
3 8 100
3 9 100
3 10 100
4 5 1
4 6 100
4 7 100
4 8 100
4 9 100
4 10 100
5 6 1
5 7 100
5 8 100
5 9 100
5 10 100
6 7 1
6 8 100
6 9 100
6 10 100
7 8 1
7 9 100
7 10 100
8 9 1
8 10 100
9 10 1
2 0 1
1 2
2 0 1
1 3
2 0 1
2 3
2 0 1
2 4
2 0 1
3 4
2 0 1
3 5
2 0 1
4 5
2 0 1
4 6
2 0 1
5 6
2 0 1
5 7
2 0 1
6 7
2 0 1
6 8
2 0 1
7 8
2 0 1
7 9
2 0 1
8 9
2 0 1
8 10
2 0 1
9 10
样例输出 #2
132

提示

样例解释:

对于全部数据, 2 ≤ n ≤ 10000 2\leq n\leq 10000 2n10000 1 ≤ m ≤ 50000 1\leq m\leq 50000 1m50000 1 ≤ s ≤ 25000 1\leq s\leq 25000 1s25000 0 ≤ k ≤ 100 0\leq k\leq 100 0k100 0 ≤ t ≤ 1 0 9 0\leq t\leq 10^9 0t109 1 ≤ c ≤ 1 0 9 1\leq c\leq 10^9 1c109 2 ≤ l ≤ n 2\leq l\leq n 2ln 0 ≤ x ≤ 1 0 9 0\leq x\leq 10^9 0x109 1 ≤ y ≤ 1 0 9 1\leq y\leq 10^9 1y109 1 ≤ a , b , v ≤ n 1\leq a,b,v\leq n 1a,b,vn ∑ l ≤ 50000 \sum l\leq 50000 l50000

子任务编号限制分数
1 k = n k=n k=n20
2 v i < v i + 1 v_i<v_{i+1} vi<vi+120
3 l = 2 l=2 l=220
4 t = 0 , x = 0 , y = 1 t=0,x=0,y=1 t=0,x=0,y=120
520
思路

按照k分层,设f[k][i]表示换成了 k 次,到达第i个点最早什么时间到。
如何转移?因为公交车线路经过哪些点是有顺序的,我们枚举每条线路,从前向后走即
可。

AC代码
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
const int N=10005,M=50005,K=105;
#define ll long long
using namespace std;
int n,m,s,k,cnt,rt[M];
ll t,INF,ans,f[M][K];
struct edg {int to;ll val;
};
struct node 
{int to;ll len;ll st,ti;
}a[M];
struct que{int x,k;ll t;bool bj;
};
vector<edg> g[N];
vector<int> p[N];
queue<que> q;
ll read()
{ll res=0;char ch=getchar();while (ch<'0'||ch>'9') ch=getchar();while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();return res;
}
void add(int x,int y,ll z,ll st,ll ti) {a[x].to=y;a[x].len=z;a[x].st=st;a[x].ti=ti;
}
bool cmp(edg x,edg y) {return x.to<y.to;
}
ll calc(ll nowt,ll st,ll ti)
{if (nowt<=st) return st;ll tn=(nowt-st)/ti;if ((nowt-st)%ti!=0) tn++;return tn*ti+st;
}
int main()
{n=read();m=read();s=read();k=read();t=read();for (int i=1;i<=m;++i){int x,y;ll z;x=read();y=read();z=read();g[x].push_back((edg){y,z});g[y].push_back((edg){x,z});}for (int i=1;i<=n;++i)sort(g[i].begin(),g[i].end(),cmp);for (int i=1;i<=s;++i){int num=read();ll x=read(),y=read();int lst=0,u=0;for (int j=1;j<=num;++j){int v;v=read();++cnt;p[v].push_back(cnt);rt[cnt]=v;if (!u) u=v,lst=cnt;else{int l=0,r=g[u].size()-1,mid,res=0;while (l<=r){mid=(l+r)>>1;if (v<=g[u][mid].to) res=mid,r=mid-1;else l=mid+1;}add(lst,cnt,g[u][res].val,x,y);lst=cnt;x+=g[u][res].val;u=v;}}}memset(f,127,sizeof(f));INF=f[0][0];for (int i=0;i<p[1].size();++i)f[p[1][i]][0]=t;for (int x=1;x<=cnt;++x){int y=a[x].to;if (!y) continue;ll nxt=calc(f[x][0],a[x].st,a[x].ti)+a[x].len;f[y][0]=min(f[y][0],nxt);}for (int j=1;j<=k;++j){for (int now=1;now<=n;++now){ll mn=INF;for (int i=0;i<p[now].size();++i)mn=min(mn,f[p[now][i]][j-1]);for (int i=0;i<p[now].size();++i)f[p[now][i]][j]=mn;}for (int x=1;x<=cnt;++x){int y=a[x].to;if (!y) continue;ll nxt=calc(f[x][j],a[x].st,a[x].ti)+a[x].len;f[y][j]=min(f[y][j],nxt);}}ans=INF;for (int i=0;i<p[n].size();++i)for (int j=0;j<=k;++j)ans=min(ans,f[p[n][i]][j]);if (ans>=INF) printf("NIE\n");else printf("%lld\n",ans);return 0;
}

这是我的第二十七篇文章,如有纰漏也请各位大佬指正
辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。


http://www.ppmy.cn/news/1518809.html

相关文章

python从入门到精通:文件操作

目录 1、文件编码 2、文件的读取 open( )打开函数 3、文件的写入 4、文件的追加 5、文件的操作&#xff08;综合案例&#xff09; 1、文件编码 因为计算机只能识别0和1&#xff0c;所以我们是通过编码技术&#xff08;密码本&#xff09;将内容翻译成0和1存入&#xff0…

python-word添加标题,段落,文字块

安装与使用python-docx 要使用必须先安装&#xff0c;要安装python-docx还是在Pycharm的终端&#xff08;Terminal&#xff09;中输入pip install python-docx&#xff0c;如下所示&#xff08;Successfully installed&#xff09;便是表示安装成功了。 新建与保存wor…

【Python基础】字符串类型

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、Python 字符串类型2.1 Python访问字符串中的值2.2 Python 转义字符2.3 Python 字符串运算符2.4 Py…

并发服务器

一、服务器 1.单循环服务器&#xff1a;同一时刻&#xff0c;只能处理一个客户端的任务&#xff1b; 2.并发服务器&#xff1a;同一时刻&#xff0c;可以处理多个客户端的任务&#xff1b; 3.TCP并发服务器&#xff1a; &#xff08;1&#xff09;多进程: &#xff08;2&a…

为什么搜索引擎可以检索到网站?

搜索引擎和爬虫&#xff0c;基于百度举例 为什么搜索引擎可以快速检索到所有对应页面&#xff1f; 搜索引擎能够快速检索到所有对应页面&#xff0c;主要归功于以下几个方面&#xff1a; 爬虫技术&#xff1a;自动遍历互联网上的网页。索引&#xff1a;将爬取的网页内容转换…

游戏出海,燃动全球,“安全”如何通关?

泼天的富贵落在了游戏圈&#xff0c;用事实打脸了男人消费不如狗的谬论。 这几天&#xff0c;无论是游戏圈内人还是圈外人&#xff0c;无人不知晓《黑神话&#xff1a;悟空》。这部头顶「3A国产游戏之光」的作品自6月8日预售以来&#xff0c;全平台销量超过800万份&#xff0c;…

【自动驾驶】控制算法(六)前馈控制与航向误差

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

【最新华为OD机试E卷】空栈压数(200分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

【机器学习】10. 朴素贝叶斯

贝叶斯理论 P ( H ∣ E ) P ( E ∣ H ) P ( H ) P ( E ) P(H|E) \frac{P(E|H)P(H)}{P(E)} P(H∣E)P(E)P(E∣H)P(H)​ 两个假设&#xff1a; 类别之间相互独立每个类别同等重要 P(E1 | yes) E1 个数 / yes 个数 0 频率问题 上述理论会遇到某个类别为0的情况&#xff0c;导致…

【Qt应用】Qt编写简易文件管理系统

目录 引言 一、准备工作 二、设计思路 三、创建项目和基本界面 四、目录浏览功能 实现效果 五、文件操作功能 5.1 设置添加文件与删除文件按钮 5.2 添加文件槽函数 5.3 删除文件槽函数 5.4 实现效果 六、文件搜索功能 6.1 准备工作 6.2 搜索按钮槽函数 6.3 实现…

Java中的注解(Annotation)

Java中的注解&#xff08;Annotation&#xff09;是一种用于在代码中添加元数据的机制。它们可以被用来为类、方法、变量、参数等元素添加额外的信息&#xff0c;这些信息在编译时或运行时可以被读取和使用。注解本身不会直接影响代码的执行&#xff0c;但可以通过反射等机制在…

汽车三元浸出液回收钯铑

汽车三元催化器是减少汽车尾气排放的关键部件&#xff0c;它含有铂、钯、铑等贵金属。这些金属在汽车尾气净化过程中起着重要作用&#xff0c;但使用一段时间后会因中毒、烧结等原因而失活。回收这些贵金属不仅可以减少环境污染&#xff0c;还能节约宝贵的资源。以下是汽车三元…

使用C标准库中的printf输出

1、增加文件系统调用 对系统调用进行了调整&#xff0c;一是将所有的系统调用实现转移 从头文件转移到C文件中&#xff1b; 二是增加几个有关文件打开和关闭的接口 主要是将系统调用做成单独的app库&#xff0c;这个库可以供其它所有的应用程序使用 2、导入newlib库&#xff…

CleanClip for Mac v2.2.0 剪贴板历史管理软件正式激活版

CleanClip 是一款专为 Mac 用户设计的强大剪贴板历史管理工具。它能够自动保存您复制的内容,让您轻松访问和管理剪贴板历史记录,大大提高工作效率。 下载地址&#xff1a;CleanClip for Mac v2.2.0 剪贴板历史管理软件正式激活版 主要特点 自动保存剪贴板历史 CleanClip 会自…

基于麻雀SSA优化BP神经网络多输入多输出的数据回归预测Matlab程序SSA-BP 含预测新数据程序

基于麻雀SSA优化BP神经网络多输入多输出的数据回归预测Matlab程序SSA-BP 含预测新数据程序 文章目录 一、基本原理1. SSA&#xff08;麻雀搜索算法&#xff09;2. BP&#xff08;反向传播神经网络&#xff09;3. SSA-BP回归预测的整合 二、实验结果三、核心代码四、代码获取五、…

disk manager操作教程 如何使用Disk Manager组件 Mac如何打开ntfs格式文件

macOS系统有一个特别明显的弱点&#xff0c;即不能对NTFS格式磁盘写入数据。想要适合Mac系统使用来回转换磁盘格式又十分麻烦&#xff0c;这该怎么办呢&#xff1f;Tuxera ntfs for mac作为一款Mac完全读写软件&#xff0c;大家在安装该软件后&#xff0c;能充分使用它的磁盘管…

python自动化脚本:让工作自动化起来

Python是一种流行的编程语言&#xff0c;以其简洁和易读性而闻名。它提供了大量的库和模块&#xff0c;使其成为自动化各种任务的绝佳选择。 我们将探讨9个Python脚本及其代码&#xff0c;可以帮助您自动化各种任务并提高工作效率。无论您是开发人员、数据分析师还是只是想简化…

跨境多账号登录如何防止IP、cookie和设备关联?

在当今数字化时代&#xff0c;拥有某个平台的多个账号是必要的&#xff0c;但如何防止这些账号之间产生关联&#xff0c;进而导致封号&#xff0c;却是一个需要谨慎对待的问题。 一、 多账号关联的主要因素 1. IP地址 2. Cookie和缓存 3. 设备指纹 二、如何防关联&#xff…

Vue——认识day06_class与style绑定

在Vue中&#xff0c;可以使用v-bind指令来将CSS样式动态地绑定到HTML元素上。有两种方式可以实现CSS与style的绑定&#xff1a; 对象语法&#xff1a;可以将一个包含CSS属性和值的对象传递给v-bind&#xff0c;将对象的属性与HTML元素的style属性进行绑定。例如&#xff1a; …

20.神经网络 - 搭建小实战和 Sequential 的使用

神经网络 - 搭建小实战和 Sequential 的使用 在 PyTorch 中&#xff0c;Sequential 是一个容器&#xff08;container&#xff09;类&#xff0c;用于构建神经网络模型。它允许你按顺序&#xff08;sequential&#xff09;添加不同的网络层&#xff0c;并将它们串联在一起&…