簡單圖論練習題OJ
問題 A: 最短路徑問題
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
給定有向圖 G,以及原點 S,請求出原點到所有點的最短路徑。
輸入
輸入檔案的第一行包含兩個整數 n, m,代表圖中的頂點數和邊數。
接下來 m 行,每行三個整數 u, v, w,代表一條從 u 指向 v,權值 為 w 的邊。
最後一行為一個整數 S。
輸出
輸出 n 個整數,依次代表 S 到 1, 2, . . . , n 的最短距離, S 到自己的距 離定義為 0,對於無法從 S 到達的點輸出 -1。整數用一個空格隔開。
樣例輸入
3 3 1 2 1 2 3 2 1 3 1 1
樣例輸出
0 1 1
提示
對於100%的資料,n ≤ 100000,m ≤ 800000,0 ≤ w ≤ 1000,請使用 dijkstra 演算法。
Dijkstra 演算法的每一次迭代分為兩個步驟:選出當前距離最小的點和 從該點更新其他點的當前最小距離。很明顯,第一步可以利用堆來選擇, 時間複雜度降至 log(n),而第二步則可能會更改堆中的某些點的權值,這 時有兩種處理方法,第一是額外記錄每個點在堆中的位置並維護;第二是 直接將一個新結點插入堆中,因為新結點比老結點的權值更小一定在老結 點之前到達堆頂,所以這樣做沒有問題,當以後碰到堆頂結點為最短距離 已確定的結點,簡單地將其丟棄即可,時間複雜度僅上升一個常數,是一 個不錯的偷懶方法。
dijkstra+堆
#include<cstdio> #include<queue> using namespace std; int read() { int ret=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar(); return ret; } const int N=1e6+5; int n,m,d[N],s; bool fl[N]; int cnt,he[N],to[N],nxt[N],w[N]; inline void add(int u,int v,int k) { to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt,w[cnt]=k; } struct NA{ int id,x; }; bool operator >(NA i,NA j) { return i.x>j.x; } priority_queue<NA,vector<NA>,greater<NA> >q; int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(),k=read(); add(u,v,k); } s=read(); for(int i=1;i<=n;i++) d[i]=2e9; q.push((NA){s,d[s]=0}); for(int i=1;i<=n;i++) { while(!q.empty()&&fl[q.top().id]) q.pop(); if(q.empty()) break; int u=q.top().id; fl[u]=1; q.pop(); for(int e=he[u];e;e=nxt[e]) { int v=to[e]; if(!fl[v]&&d[v]>d[u]+w[e]) q.push((NA){v,d[v]=d[u]+w[e]}); } } for(int i=1;i<n;i++) if(d[i]!=2e9) printf("%d ",d[i]); else printf("-1 "); if(d[n]!=2e9) printf("%d",d[n]); else printf("-1"); return 0; }
問題 B: 最短路徑問題(二)
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
給定有向圖 G,以及原點 S,請求出原點到所有點的最短路徑。
輸入
輸入檔案的第一行包含兩個整數 n, m,代表圖中的頂點數和邊數。
接下來 m 行,每行三個整數 u, v, w,代表一條從 u 指向 v,權值 為 w 的邊。
最後一行為一個整數 S。
輸出
若圖中存在負權環,則輸出一行“ERROR”。
否則輸出 n 個整數,依次代表 S 到 1, 2, . . . , n 的最短距離, S 到自己 的距離定義為 0,對於無法從 S 到達的點輸出 -1。整數用一個空格隔開。
樣例輸入
樣例輸出
提示
對於所有資料, n ≤ 1000, m ≤ 100000, −1000 ≤ w ≤ 1000。
SPFA模擬題
#include<cstdio>
using namespace std;
int read()
{
int ret=0; char ch=getchar(); bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return f?-ret:ret;
}
const int N=1e6+5;
int n,m,d[N],s,l,r,q[N],num[N];
int cnt,he[N],to[N],nxt[N],w[N];
inline void add(int u,int v,int k)
{
to[++cnt]=v,nxt[cnt]=he[u],w[cnt]=k,he[u]=cnt;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),k=read();
add(u,v,k);
}
s=read();
for(int i=1;i<=n;i++) d[i]=1e9;
l=1,r=1,q[1]=s; d[s]=0; num[s]=1;
bool f=0;
while(l<=r)
{
for(int e=he[q[l]];e;e=nxt[e])
{
int v=to[e];
if(d[v]>d[q[l]]+w[e])
{
d[v]=d[q[l]]+w[e],num[v]++;
if(num[v]==n)
{
printf("ERROR");
f=1; break;
}
q[++r]=v;
}
}
if(f) break;
l++;
}
if(!f)
{
for(int i=1;i<n;i++)
if(d[i]!=1e9) printf("%d ",d[i]);
else printf("-1 ");
if(d[n]!=1e9) printf("%d",d[n]);
else printf("-1");
}
return 0;
}
問題 C: 最小圈
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
給定一個帶權無向圖 G = (V, E),請求出其中的最小圈。圈定義為 頂點序列 (v1,v2,...,vp,vp+1),滿足 vi = vj(i = j), v1 = vp+1,且邊 (vi, vi+1) ∈ E。
輸入
第一行為兩個整數 n,m,代表圖的頂點數和邊數。
接下來 m 行,每行三個整數 u,v,w,描述一條連線 u 和 v,權值為 w 的邊。
輸出
輸出一個整數,即圖中的最小圈。資料保證圖中至少存在一個圈。
樣例輸入
6 7 1 2 1 2 4 2 4 6 3 5 6 4 3 5 5 3 4 2 1 3 6
樣例輸出
11
提示
對於30%的資料n ≤ 30 對於 60% 的資料, n ≤ 100 對於 100% 的資料, n ≤ 400, w ≤ 100000
利用 Floyd 演算法,求得環中的點最大編號為 1, 2, . . . , n 時的最小環。
模板題
#include<cstdio>
#include<iostream>
using namespace std;
int read()
{
int ret=0; char ch=getchar(); bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return f?-ret:ret;
}
const int N=405;
int n,m,f[N][N],a[N][N][2],ans;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) a[i][j][0]=a[i][j][1]=f[i][j]=6e8;
for(int i=1;i<=n;i++) f[i][i]=0;
ans=2e9;
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
f[u][v]=f[v][u]=min(f[u][v],w);
if(a[u][v][0]!=6e8) a[u][v][0]=a[v][u][0]=w;
else if(a[u][v][1]!=6e8) a[u][v][1]=a[v][u][1]=w;
else
{
if(a[u][v][0]>w)
a[u][v][1]=a[v][u][1]=a[u][v][1],a[u][v][0]=a[v][u][0]=w;
else a[u][v][1]=a[v][u][1]=min(a[u][v][1],w);
}
ans=min(ans,a[u][v][0]+a[u][v][1]);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i!=j&&f[i][j]!=6e8&&a[i][k][0]!=6e8&&a[j][k][0]!=6e8) ans=min(ans,f[i][j]+a[i][k][0]+a[k][j][0]);
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
printf("%d",ans);
return 0;
}
問題 D: 公路修建
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
有 n 個城市需要用道路連線起來,現在有 m 條道路的修建方案,每 個方案的實施可以帶來一定的利潤,你的任務是確定實施的方案,使得任 意兩個城市都連通,並且獲得的利潤最大。
輸入
第一行為兩個整數 n, m,含義如題中所述。n ≤ 10000, m ≤ 200000。
接下來有 m 行,每行三個整數 u, v, w,代表在 u 和 v 之間修建一 條道路可以獲得 w 的利潤。注意,修建一條道路可能會導致虧損,這時我 們用負的利潤表示。
輸出
輸出一行,代表能夠獲得的最大利益。
樣例輸入
3 3 1 2 -1 2 3 -2 1 3 -1
樣例輸出
-2
>=0的邊全部加上,其餘的最大生成樹處理
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int read()
{
int ret=0; char ch=getchar(); bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return f?-ret:ret;
}
const int N=1e6+5;
int n,m,ff[N];
ll ans;
struct NA{
int u,v,w;
}e[N];
bool cmp(NA i,NA j)
{
return i.w>j.w;
}
int find(int x)
{
return x==ff[x]?x:ff[x]=find(ff[x]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
e[i].u=read(),e[i].v=read(),e[i].w=read();
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++) ff[i]=i;
for(int i=1;i<=m;i++)
if(e[i].w>=0)
{
int u=e[i].u,v=e[i].v;
int f1=find(u),f2=find(v);
ans+=e[i].w,ff[f1]=f2;
} else
{
int u=e[i].u,v=e[i].v;
int f1=find(u),f2=find(v);
if(ff[f1]!=f2) ans+=e[i].w,ff[f1]=f2;
}
printf("%lld",ans);
return 0;
}
問題 E: 燒錄光碟
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
在 JSOI2005 夏令營快要結束的時候,很多營員提出來要把整個夏令 營期間的資料刻錄成一張光碟給大家,以便大家回去後繼續學習。組委會 覺得這個主意不錯!可是組委會一時沒有足夠的空光碟,沒法保證每個人 都能拿到刻錄上資料的光碟,又來不及去買了,怎麼辦呢?!
組委會把這個難題交給了 LHC,LHC 分析了一下所有營員的地域關 系,發現有些營員是一個城市的,其實他們只需要一張就可以了,因為一 個人拿到光碟後,其他人可以帶著 U 盤之類的東西去拷貝啊!
可是,LHC 調查後發現,由於種種原因,有些營員並不是那麼的合 作,他們願意某一些人到他那兒拷貝資料,當然也可能不願意讓另外一些 人到他那兒拷貝資料,這與我們 JSOI 宣揚的團隊合作精神格格不入!
現在假設總共有 N 個營員(2<=N<=2000),每個營員的編號為 1 N。 LHC 給每個人發了一張調查表,讓每個營員填上自己願意讓哪些人到他那 兒拷貝資料。當然,如果 A 願意把資料拷貝給 B,而 B 又願意把資料拷 貝給 C,則一旦 A 獲得了資料,則 B,C 都會獲得資料。
現在,請你編寫一個程式,根據回收上來的調查表,幫助 LHC 計算 出組委會至少要燒錄多少張光碟,才能保證所有營員回去後都能得到夏令 營資料?
輸入
先是一個數 N,接下來的 N 行,分別表示各個營員願意把自己獲得的 資料拷貝給其他哪些營員。即輸入資料的第 i+1 行表示第 i 個營員願意把 資料拷貝給那些營員的編號,以一個 0 結束。如果一個營員不願意拷貝資 料給任何人,則相應的行只有 1 個 0,一行中的若干數之間用一個空格隔 開。
輸出
一個正整數,表示最少要刻錄的光碟數。
樣例輸入
5 2 4 3 0 4 5 0 0 0 1 0
樣例輸出
1
提示
tarjan縮點後統計度為0的結點
#include<cstdio>
#include<iostream>
using namespace std;
int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret;
}
const int N=2005,M=4000005;
int n,m,d[N],ans;
int cnt,he[M],to[M],nxt[M];
struct NA{
int u,v;
}e[M];
int sgn,dfn[N],low[N],top,st[N],col,co[N];
inline void add(int u,int v)
{
to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}
void tar(int u)
{
dfn[u]=low[u]=++sgn;
st[++top]=u;
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(!dfn[v])
tar(v),low[u]=min(low[u],low[v]);
else if(!co[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
co[u]=++col;
while(top&&st[top]!=u)
co[st[top--]]=col;
top--;
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
int x=read();
while(x)
e[++m].u=i,e[m].v=x,
add(i,x),x=read();
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tar(i);
for(int i=1;i<=m;i++)
if(co[e[i].u]!=co[e[i].v]) d[co[e[i].v]]++;
ans=0;
for(int i=1;i<=col;i++)
if(!d[i]) ans++;
printf("%d",ans);
return 0;
}
問題 F: 割點和橋
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
給定連通無向圖 G,請求出 G 中的割點和橋的個數。
輸入
第一行兩個整數 n,m,代表圖的頂點數和邊數。 接下來 m 行,每行兩個整數 u, v,描述一條無向邊。 N ≤ 50000, m ≤ 200000
輸出
輸出兩個整數,先輸出割點的數量和再輸出橋的數量,用一個空格隔 開。
樣例輸入
4 5 1 2 1 2 2 3 2 3 3 4
樣例輸出
2 1
#include<cstdio>
#include<iostream>
using namespace std;
int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret;
}
const int N=1e6+5;
int n,m,num[N],ans;
int cnt,he[N],to[N],nxt[N];
int sgn,low[N],dfn[N];
inline void add(int u,int v)
{
to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}
void tar1(int u,int r)
{
low[u]=dfn[u]=++sgn;
int ch=0;
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(!dfn[v])
{
tar1(v,r); low[u]=min(low[u],low[v]);
if(u!=r&&low[v]>=dfn[u]) num[u]=1;
if(u==r) ch++;
}
low[u]=min(low[u],dfn[v]);
}
if(u==r&&ch>=2) num[u]=1;
}
void tar2(int fa,int u)
{
low[u]=dfn[u]=++sgn;
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(v!=fa)
{
if(!dfn[v])
{
tar2(u,v); low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]) ans++;
}
low[u]=min(low[u],dfn[v]);
} else fa=0;
}
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
add(u,v); add(v,u);
}
tar1(1,1);
for(int i=1;i<=n;i++)
if(num[i]) ans++;
printf("%d ",ans);
sgn=ans=0;
for(int i=1;i<=n;i++)
dfn[i]=low[i]=0;
tar2(0,1);
printf("%d",ans);
return 0;
}