图论模版
目录
1.强联通
判断图是否强联通
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 10002
using namespace std;
int n,m,head[maxn],t1,t2,tot;
int low[maxn],dfn[maxn],zh[maxn],top,sc,ins[maxn],cnt;
struct node{
int v,nex;
}e[100002];
int ss(){
int v=0;char ch;
while(!isdigit(ch=getchar()));v=v+ch-48;
while(isdigit(ch=getchar()))v=v*10+ch-48;
return v;
}
void lj(int t1,int t2){
e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void tarjan(int k){
dfn[k]=low[k]=++sc;
zh[++top]=k;ins[k]=1;
for(int i=head[k];i;i=e[i].nex){
if(!dfn[e[i].v]){
tarjan(e[i].v);
low[k]=min(low[k],low[e[i].v]);
}
else if(ins[e[i].v])low[k]=min(low[k],dfn[e[i].v]);
}
if(dfn[k]==low[k]){
cnt++;if(cnt>1)return;
while(top>0){
ins[zh[top]]=0;
if(zh[top]==k){top--;break;}
top--;
}
}
}
void Q(){
for(int i=1;i<=10000;i++)head[i]=low[i]=dfn[i]=0;
sc=0,tot=0;cnt=0;top=0;
}
int main(){
while(~scanf("%d%d",&n,&m)&&n){
Q();
for(int i=1;i<=m;i++){
t1=ss();t2=ss();
lj(t1,t2);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
if(cnt>1)puts("No");
else puts("Yes");
}
return 0;
}
2.割点
hdu3671 Boonie and Clyde
给一个无向图,要求毁掉两个点,使图变得不连通,图一开始是连通的
做法:先枚举要删除的第1个点,在原图中删除它,看看删除它后整个图的变化
1.整个图变得不连通了(即这个点本身是割点),但是还没完要分类讨论一下
(1).整个图变为两部分,但是两部分刚好都是一个点,那么这两个点再毁掉哪个点都好,图的连通分支数都不会增加,这是一个特殊情况
例如,(1,2)(2,3)这种图,是无解的,任意毁掉两个点都无法增加图的连通分支,所以方案数为0
(2).整个图分为两部分,但是有一部分的点数为1,另一部分大于1,那么这时候只要在较大的那部分,任意毁掉一个点(无论是不是割点都行),最后整个图都会至少被分为了两个部分
(如果毁掉的是割点,将分成更多份),所以这样产生的方案数是V-2
(3).整个图分为了两个部分,两个部分的点数都大于1,那么任意在哪个部分毁掉那个点都可以(无论是不是割点都行),最后整个图都会至少分为两个部分,所以方案数为V-1
(4).整个图被分为了三个或更多的部分,那么也是在剩下的点中任意毁掉一个点都可以(无论那个点是不是割点),方案数为V-1
(如果这个点刚好处于一个部分且这个部分只有它自己一个点,那么 毁掉后整个图的分支数减1;如果这个点在一个部分且这个部分不止它一个点且这个点不是割点,那么分支数 不会增加,如果是割点分支数为增加)
2.删除第一个点后,整个图还是连通的(是连通,不是双连通)
那么就在剩下的图中找割点,找到几个,方案数就是多少
来自https://www.cnblogs.com/scau20110726/archive/2013/05/22/3092078.html
割点条件:
1.k是树根且有两棵以上子树
2.low[e[i].v]>=dfn[k]
记的判断e[i].v!=fa
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 1005
using namespace std;
int n,m,head[maxn],tot,t1,t2,now;
int dfn[maxn],low[maxn],flag[maxn],sum,sc,T;
struct node
{
int v,nex;
}e[20002];
void lj(int t1,int t2){
tot++;e[tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void Q1(){
tot=0;
memset(head,0,sizeof head);
}
void Q2(){
for(int i=1;i<=n;i++)low[i]=dfn[i]=flag[i]=0;
sum=0;sc=0;
}
int tarjan(int k,int fa)
{
dfn[k]=low[k]=++sc;
int kid=0,ss=0;
for(int i=head[k];i;i=e[i].nex){
if(e[i].v==now)continue;
if(!dfn[e[i].v]){
ss+=tarjan(e[i].v,k);kid++;
low[k]=min(low[k],low[e[i].v]);
if(fa==0&&kid==2)flag[k]=1;
if(fa!=0&&low[e[i].v]>=dfn[k])flag[k]=1;
}
else if(e[i].v!=fa){
low[k]=min(low[k],dfn[e[i].v]);
}
}
sum+=flag[k];
return ss+1;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n){
Q1();
for(int i=1;i<=m;i++){
scanf("%d%d",&t1,&t2);
lj(t1,t2);lj(t2,t1);
}
int ans=0;
for(now=1;now<=n;now++){
Q2();int co=0,ff=0;
for(int i=1;i<=n;i++){
if(!dfn[i]&&i!=now){
int fsy=tarjan(i,0);
co++;
if(fsy==1)ff++;
}
}
if(co>2)ans=ans+n-1;
else if(co==2){
if(ff==2)ans-=2;
if(ff==1)ans--;
ans=ans+n-1;
}
else ans=ans+sum;
//cout<<ans<<' '<<co<<' '<<ff<<endl;
}
printf("Case %d: %d
",++T,ans/2);
}
return 0;
}
3.割边
hdu4738 Caocao's Bridges
题意 :求最小桥,注意,这题如果不连通输出0
连通的话,如果,最小桥是0刚要输出1,因为,必须派一个人
注意重边!!!!
if(fa==(i^1))continue;
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 1002
using namespace std;
int n,m,head[maxn],t1,t2,t3,tot=1;
int dfn[maxn],low[maxn],sc,Min,co;
struct node{
int v,w,nex;
}e[4000006];
void lj(int t1,int t2,int t3)
{
e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot;
}
void tarjan(int k,int fa)
{
//co++;
dfn[k]=low[k]=++sc;
for(int i=head[k];i;i=e[i].nex){
// if(e[i].v==fa)continue; FUCK CHONGBIAN
if(fa==(i^1))continue;
if(!dfn[e[i].v]){
tarjan(e[i].v,i);
low[k]=min(low[k],low[e[i].v]);
if(low[e[i].v]>dfn[k])Min=min(Min,e[i].w);
}
else low[k]=min(low[k],dfn[e[i].v]);
}
}
void Q(){
for(int i=1;i<=1000;i++)dfn[i]=low[i]=0,head[i]=0;
tot=1,sc=0;co=0;
}
int main(){
while(~scanf("%d%d",&n,&m) &&n){
Q();
for(int i=1;i<=m;i++){
scanf("%d%d%d",&t1,&t2,&t3);
lj(t1,t2,t3);lj(t2,t1,t3);
}
Min=1e9;co=0;
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i,0),co++;
}
if(co>1)puts("0");
else if(Min==1e9)puts("-1");
else if(Min==0)puts("1");
else cout<<Min<<endl;
}
return 0;
}
4.点双
例题
https://blog.csdn.net/liankewei123456/article/details/81623907
记的是保存边
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 1002
#define M 2000006
using namespace std;
int n,m,t1,t2,head[maxn],fl[maxn][maxn],tot;
int dfn[maxn],low[maxn],zh[M],top,sc,cnt,he[maxn],tt;
int flag[maxn],co[maxn],fsy[maxn];
struct node{
int nex,u,v;
}e[M],h[M];
void lj(int t1,int t2){
e[++tot].v=t2;e[tot].u=t1;e[tot].nex=head[t1];head[t1]=tot;
}
void add(int t1,int t2){
h[++tt].v=t2,h[tt].nex=he[t1];he[t1]=tt;
}
void lian(int u,int v){
cnt++;
while(top>0){
t1=e[zh[top]].u,t2=e[zh[top]].v;
add(cnt,t1);add(cnt,t2);
if(t1==u&&t2==v){top--;break;}
top--;
}
}
void tarjan(int k,int fa){
dfn[k]=low[k]=++sc;
// cout<<k<<' '<<dfn[k]<<endl;
for(int i=head[k];i;i=e[i].nex){
//cout<<"fsy "<<e[i].v<<endl;
if(e[i].v==fa)continue;
if(!dfn[e[i].v]){
zh[++top]=i;
tarjan(e[i].v,k);
low[k]=min(low[k],low[e[i].v]);
if(low[e[i].v]>=dfn[k])lian(k,e[i].v);//geding
}
else{
if(low[k]>dfn[e[i].v]){
low[k]=dfn[e[i].v];
zh[++top]=i;
}
}
}
// cout<<"aa "<<k<<' '<<low[k]<<endl;
}
bool pd(int k){
for(int i=head[k];i;i=e[i].nex){
if(!flag[e[i].v])continue;
if(!co[e[i].v]){
co[e[i].v]=3-co[k];
if(!pd(e[i].v))return 0;
}
else if(co[e[i].v]!=3-co[k])return 0;
}
return 1;
}
void Q()
{
sc=tot=tt=0;
for(int i=1;i<=1000;i++)
head[i]=he[i]=dfn[i]=low[i]=fsy[i]=0;
memset(fl,0,sizeof fl);
memset(e,0,sizeof e);memset(h,0,sizeof h);
}
int main(){
while(~scanf("%d%d",&n,&m)&&n){
Q();
for(int i=1;i<=m;i++){
scanf("%d%d",&t1,&t2);
fl[t1][t2]=fl[t2][t1]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(!fl[i][j]&&i!=j)lj(i,j);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i,0);
}
for(int x=1;x<=cnt;x++){
memset(flag,0,sizeof flag);
memset(co,0,sizeof co);
for(int i=he[x];i;i=h[i].nex)flag[h[i].v]=1;
//cout<<x<<endl;
//for(int i=he[x];i;i=h[i].nex)cout<<h[i].v<<' ';cout<<endl;
int S=h[he[x]].v;co[S]=1;
if(!pd(S)){
for(int i=he[x];i;i=h[i].nex)fsy[h[i].v]=1;
}//youjihuan keyicanjia
}
int ans=n;
for(int i=1;i<=n;i++)ans-=fsy[i];
cout<<ans<<endl;
}
return 0;
}
5.边双
poj 3352 Road Construction
给定一个连通的无向图G,至少要添加几条边才能使所给无向图图变成边双连通图。
先把边双缩成点,然后把叶子连起来即可
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 5002
using namespace std;
int n,m,head[maxn],tot=1,t1,t2;
int dfn[maxn],low[maxn],sc,zh[maxn],top,dy[maxn],cnt;
int in[maxn];
bool ins[maxn];
struct node
{
int v,nex,bri;
}e[20002];
void lj(int t1,int t2){
e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void tarjan(int k,int fa){
dfn[k]=low[k]=++sc;
zh[++top]=k,ins[k]=1;
for(int i=head[k];i;i=e[i].nex){
if(e[i].v==fa)continue;//important
if(!dfn[e[i].v]){
tarjan(e[i].v,k);
low[k]=min(low[k],low[e[i].v]);
if(low[e[i].v]>dfn[k]){
e[i].bri=e[i^1].bri=1;
}
}
else if(ins[e[i].v]){
low[k]=min(low[k],dfn[e[i].v]);
}
}
if(dfn[k]==low[k]){
cnt++;
while(top>0){
dy[zh[top]]=cnt;
ins[zh[top]]=0;
if(zh[top]==k){top--;break;}
top--;
}
}
}
void Q()
{
tot=1;top=cnt=sc=0;
for(int i=1;i<=5000;i++)dfn[i]=low[i]=dy[i]=in[i]=0;
memset(e,0,sizeof e);
}
int main(){
while(~scanf("%d%d",&n,&m)){
Q();
for(int i=1;i<=m;i++){
scanf("%d%d",&t1,&t2);
lj(t1,t2);lj(t2,t1);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])tarjan(i,0);
}
for(int i=1;i<=tot;i++){
if(e[i].bri==1)in[dy[e[i].v]]++;
}
int sum=0;
for(int i=1;i<=cnt;i++)if(in[i]==1)sum++;
cout<<(sum+1)/2<<endl;
}
return 0;
}
6.2-set
https://blog.csdn.net/liankewei123456/article/details/81627791 裸题
POJ 2723 Get Luffy Out
有n对钥匙,m个门,每对钥匙用了其中1个,另一个就会消失,每个门上有m个锁,用特定的钥匙打开其中1个锁,另一个锁会消失,连续的打开门,问你之多能打开几扇门?
二分门数,2-set判断是否合法
为什么只有我2-set写dfs
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 25002
using namespace std;
int n,m,head[maxn],tot,l,r,mid,t1,t2,zh[maxn],top;
bool mark[maxn];
struct node{
int v,nex;
}e[3000002];
struct no{
int x,y;
}key[maxn],d[maxn];
void lj(int t1,int t2){
e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void Q(){
tot=0;
for(int i=1;i<=25000;i++)mark[i]=head[i]=0;
}
bool dfs(int k)
{
if(mark[k])return 1;
if(mark[k^1])return 0;
mark[k]=1;zh[++top]=k;
for(int i=head[k];i;i=e[i].nex){
if(!dfs(e[i].v))return 0;
}
return 1;
}
bool pd(int k){
Q();
for(int i=1;i<=n;i++){
t1=2*key[i].x+1,t2=2*key[i].y;
lj(t1,t2);
t1=2*key[i].y+1,t2=2*key[i].x;
lj(t1,t2);
}
for(int i=1;i<=k;i++){
t1=2*d[i].x,t2=2*d[i].y+1;
lj(t1,t2);
t1=2*d[i].y,t2=2*d[i].x+1;
lj(t1,t2);
}
for(int i=1;i<=n;i++){
t1=i*2,t2=i*2+1;
if(!mark[t1]&&!mark[t2]){
top=0;
if(!dfs(t1)){
while(top>0){
mark[zh[top--]]=0;
}
if(!dfs(t2))return 0;
}
}
}
return 1;
}
int main(){
while(~scanf("%d%d",&n,&m)&&n){
for(int i=1;i<=n;i++){
scanf("%d%d",&key[i].x,&key[i].y);
key[i].x++,key[i].y++;
}
for(int i=1;i<=m;i++){
scanf("%d%d",&d[i].x,&d[i].y);
d[i].x++;d[i].y++;
}
int l=0,r=m;
while(l<r){
mid=(l+r+1)/2;
if(pd(mid))l=mid;
else r=mid-1;
}
printf("%d
",l);
}
return 0;
}
7.dijstra+heap
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 10005
#define inf 1e9
using namespace std;
int n,m,kk,head[maxn],t1,t2,t3,tot;
int d[maxn][22],flag[maxn][22];
struct node{
int v,nex,w;
}e[100005];
struct no{
int x,num,dist;
};
bool operator <(no a,no b){
return a.dist>b.dist;
}
priority_queue<no>q;
void lj(int t1,int t2,int t3){
e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1],head[t1]=tot;
}
int main()
{
cin>>n>>m>>kk;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&t1,&t2,&t3);
lj(t1,t2,t3);lj(t2,t1,t3);
}
for(int i=1;i<=n;i++)
for(int j=0;j<=kk;j++)d[i][j]=inf;
q.push((no){1,0,0});d[1][0]=0;
while(!q.empty()){
no k=q.top();q.pop();
while(flag[k.x][k.num]&&!q.empty())k=q.top(),q.pop();
if(flag[k.x][k.num]&&q.empty())break;
flag[k.x][k.num]=1;
for(int i=head[k.x];i;i=e[i].nex){
if(d[e[i].v][k.num]>d[k.x][k.num]+e[i].w&&!flag[e[i].v][k.num]){
d[e[i].v][k.num]=d[k.x][k.num]+e[i].w;
q.push((no){e[i].v,k.num,d[e[i].v][k.num]});
}
if(k.num+1<=kk&&d[e[i].v][k.num+1]>d[k.x][k.num]&&!flag[e[i].v][k.num+1]){
d[e[i].v][k.num+1]=d[k.x][k.num];
q.push((no){e[i].v,k.num+1,d[e[i].v][k.num+1]});
}
}
}
int ans=1e9;
for(int i=0;i<=kk;i++)ans=min(ans,d[n][i]);
cout<<ans<<endl;
return 0;
}
注意判定条件 T.dist<dist