P1462 通往奧格瑞瑪的道路
題目:找最短路徑中點權的最大值的最小值
最大值的最小值,最小值的最大值,一般這種題目都會想到二分答案去解決。
所以我們先把所有點權排個序(二分需滿足有序性),然後二分點權,跑一遍最短路確認該方案是否可行(走的路徑時,邊權損失的HP要儘量的小,這樣纔可能到達最終的點,維護答案即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define int long long
#define ll int
const int maxn = 1e6+5;
struct Edge{
int u,v,nxt,c;
}edge[maxn<<1];
struct Node{
int u,dis;
bool operator <(const Node &h)const{
return dis > h.dis;
}
};
int head[maxn],tot;
inline void init(){
memset(head,-1,sizeof(head));
tot = 0;
}
inline void addedge(int u,int v,int c){
++tot;
edge[tot] = {u,v,head[u],c};
head[u] = tot;
}
int f[maxn],ff[maxn];
int dis[maxn];
bool vis[maxn];
bool dijiastra(int u,int v,int fm,int hp){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[u] = 0;
if(fm < ff[u]) return false;
priority_queue<Node> pq; pq.push(Node{u,0});
while(!pq.empty()){
Node x = pq.top(); pq.pop();
if(vis[x.u]) continue;
vis[x.u] = true;
for(int i = head[x.u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
if(ff[e.v] > fm) continue;
if(dis[e.v] > dis[x.u] + e.c){
dis[e.v] = dis[x.u] + e.c;
pq.push(Node{e.v,dis[e.v]});
}
}
}
return dis[v] <= hp;
}
signed main()
{
init();
int n,m,b; cin >> n >> m >> b;
for(int i = 1; i <= n; ++i){
cin >> f[i]; ff[i] = f[i];
}
sort(f+1,f+n+1);
for(int i = 0; i < m; ++i){
int u,v,c; cin >> u >> v >> c;
addedge(u,v,c);
addedge(v,u,c);
}
if(!dijiastra(1,n,f[n],b)){
cout << "AFK" << endl;
return 0;
}
int l = 1,r = n;
int ans = 1e9;
while(l <= r){
int mid = l + r >> 1;
if(dijiastra(1,n,f[mid],b)){
ans = f[mid];
r = mid-1;;
}
else l = mid+1;
}
cout << ans << endl;
return 0;
}
題意:
在一個神奇的小鎮上有着一個特別的電車網絡,它由一些路口和軌道組成,每個路口都連接着若干個軌道,每個軌道都通向一個路口(不排除有的觀光軌道轉一圈後返回路口的可能)。在每個路口,都有一個開關決定着出去的軌道,每個開關都有一個默認的狀態,每輛電車行駛到路口之後,只能從開關所指向的軌道出去,如果電車司機想走另一個軌道,他就必須下車切換開關的狀態。
爲了行駛向目標地點,電車司機不得不經常下車來切換開關,於是,他們想請你寫一個程序,計算一輛從路口A到路口B最少需要下車切換幾次開關。
轉換成最短路來考慮問題,如果下來需要切換開關,那麼u->v相當於邊權就賦予了1,反之邊權就爲0,跑一遍最短路就可以得到答案了
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
struct Edge{
int u,v,nxt;
}edge[maxn];
struct Node{
int u,dis;
bool operator <(const Node &h)const{
return dis > h.dis;
}
};
int head[maxn],tot;
inline void init(){
memset(head,-1,sizeof(head));
tot = 0;
}
inline void addedge(int u,int v){
++tot;
edge[tot] = {u,v,head[u]};
head[u] = tot;
}
int state[maxn]; // 每個點的開關狀態
int dis[maxn]; bool vis[maxn];
const int inf = 0x3f3f3f3f;
void dijiastra(int s,int t){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s] = 0;
priority_queue<Node> pq; pq.push(Node{s,0});
while(!pq.empty()){
Node x = pq.top(); pq.pop();
if(vis[x.u]) continue; vis[x.u] = true;
for(int i = head[x.u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
int tag = (state[x.u]==e.v? 0 : 1);
if(dis[e.v] > dis[x.u] + tag){
dis[e.v] = dis[x.u] + tag;
pq.push(Node{e.v,dis[e.v]});
}
}
}
if(dis[t]==inf) cout << -1 << endl;
else cout << dis[t] << endl;
}
int main()
{
init();
int n,a,b; cin >> n >> a >> b;
for(int i = 1; i <= n; ++i){
int k; cin >> k;
for(int j = 0; j < k; ++j){
int x; cin >> x;
if(!j) state[i] = x;
addedge(i,x);
}
}
dijiastra(a,b);
return 0;
}
最短路Floyed
(看了一波別人大佬的題解) 對floyed有了新的認識,原來覺得floyed由於的複雜度,覺得適用面一直挺窄的,但其實是自己對其的理解程度還不夠。
這個算法的主要思路,就是通過其他的點進行中轉來求的兩點之間的最短路。因爲我們知道,兩點之間有多條路,如果換一條路可以縮短距離的話,就更新最短距離。而它最本質的思想,就是用其他的點進行中轉,從而達到求出最短路的目的。
那麼,如何進行中轉呢?兩點之間可以由一個點作爲中轉點更新最短路徑,也可以通過多個點更新最短路徑。
結合代碼:
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];
//核心代碼,僅僅只有5行
這段代碼的基本思想就是:最開始只允許經過1號頂點進行中轉,接下來只允許經過1和2號頂點進行中轉……允許經過1~n號所有頂點進行中轉,求任意兩點之間的最短路程。用一句話概括就是:從i號頂點到j號頂點只經過前k號點的最短路程。
P1119 災後重建
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2e2+5;
int day[maxn];
int dp[205][205];
inline void floyed(int k,int n){
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(dp[i][j] > dp[i][k] + dp[j][k]){
dp[i][j] = dp[j][i] = dp[i][k] + dp[j][k];
}
}
}
}
const int inf = 0x3f3f3f3f;
int main()
{
memset(dp,0x3f,sizeof(dp));
int n,m; cin >> n >> m;
for(int i = 0; i < n; ++i) cin >> day[i];
for(int i = 0; i < m; ++i){
int u,v,w; cin >> u >> v >> w;
dp[u][v] = w; dp[v][u] = w;
}
for(int i = 0; i < n; ++i)
dp[i][i] = 0;
int now = 0;
int Q; cin >> Q;
for(int i = 0; i < Q; ++i){
int x,y,t; cin >> x >> y >> t;
while(day[now] <= t && now < n){
floyed(now,n); ++now;
}
if(day[x] > t || day[y] > t){
cout << -1 << endl;
}else{
if(dp[x][y]==inf){
cout << -1 << endl;
}
else cout << dp[x][y] << endl;
}
}
return 0;
}
2019南昌網絡邀請賽B題(這裏採用了虛擬結點連法)
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e6+1e5;
int v,e,ss,k,c;
struct Edge{
int u,v,nxt,w;
}edge[maxn];
struct Node{
int u,dis;
bool operator < (const Node &h)const{
return dis > h.dis;
}
};
int tot,head[1005];
inline void addedge(int u,int v,int w){
++tot;
edge[tot] = {u,v,head[u],w};
head[u] = tot;
}
int dis[1005];
bool vis[1005];
int heroMax,xfdd,xfdw[1005],tot2;
void dijiastra(int s,int n){
memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis));
dis[s] = 0;
priority_queue<Node> pq; pq.push(Node{s,0});
while(!pq.empty()){
Node x = pq.top(); pq.pop();
if(vis[x.u]) continue;
vis[x.u] = true;
for(int i = head[x.u]; ~i; i = edge[i].nxt){
Edge & e = edge[i];
if(!vis[e.v] && dis[e.v] > x.dis + e.w){
dis[e.v] = x.dis + e.w;
pq.push(Node{e.v,dis[e.v]});
}
}
}
}
inline void init(){
memset(head,-1,sizeof(head));
heroMax = 0;
xfdd = 0;
tot = 0;
}
int main()
{
int T; scanf("%d",&T);
while(T--){
init();
scanf("%d%d%d%d%d",&v,&e,&ss,&k,&c);
int nn = v+1;
for(int i = 0; i < k; ++i){
int x; scanf("%d",&x);
xfdw[i] = x;
addedge(nn,x,0);
}
for(int i = 0; i < e; ++i){
int u,v,w; scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dijiastra(nn,v);
for(int j = 1; j <= v; ++j) xfdd = max(xfdd,dis[j]);
dijiastra(ss,v);
for(int i = 1; i <= v; ++i){
heroMax = max(heroMax,dis[i]);
}
if(heroMax <= xfdd*c){
printf("%d\n",heroMax);
}
else printf("%d\n",xfdd);
}
return 0;
}