图论板子

1.spfa
队列

#include 
#include
#include
#include
#include 
#include
using namespace std;
int n,m,s;
int end;
int head[500005];
bool vis[10005];
int dis[10005];
const int inf=2147483647;
struct p{
 int next,to,dis;
};
p a[500005];
int cnt=0;
//链式前向星 这里是双向图 所以存两次
void add(int from,int to,int dis){
 a[++cnt].next=head[from];
 a[cnt].to=to;
 a[cnt].dis=dis;
 head[from]=cnt;
  
 a[++cnt].next=head[to];
 a[cnt].to=from;
 a[cnt].dis=dis;
 head[to]=cnt;
}

void spfa(){
 queue<int> q;
 for(int i=1;i<=n;i++){
  dis[i]=inf;
  vis[i]=0;
 } 
 //vis是入队标记 1为入队
 q.push(s); dis[s]=0;vis[s]=1;
 while(!q.empty()){
  int u=q.front();
  q.pop(); vis[u]=0;//出队啊
  for(int i=head[u];i;i=a[i].next){
   int v=a[i].to;
   if(dis[v]>dis[u]+a[i].dis){
    dis[v]=dis[u]+a[i].dis;
    //如果可以松弛,那么经过这个的需要更新 
    //没入对的话就入队
    if(!vis[v]){
     vis[v]=1;
     q.push(v);
    }
   } 
  }
 }
}
int main(){
cin>>n>>m>>s>>end;
for(int i=1;i<=m;i++){
 int f,g,w;
 cin>>f>>g>>w;
 add(f,g,w);
} 
spfa();
cout<<dis[end]<<endl;
}

2.floyd
由于复杂度的原因,私以为是比赛中不会出现的算法
但是确实相当好写,非常令人愉悦

#include 
#include
#include
#include
#include 
#include
int dis[104][103];
int p[104];
int ans=999999;
const int inf=9999999;
using namespace std;
int main(){
 int n;
 cin>>n;
 for(int i=1;i<=n;i++){
  for(int j=1;j<=n;j++){
   if(i!=j) dis[i][j]=inf;
  }
 }
 for(int i=1;i<=n;i++){
  int x,y,z;
  cin>>x>>y>>z;
  p[i]=x;
  if(y) {
   dis[i][y]=1;
   dis[y][i]=1;
  }
  if(z){
   dis[i][z]=1;
   dis[z][i]=1;
  }
 }
 for(int k=1;k<=n;k++){
  for(int i=1;i<=n;i++){
   for(int j=1;j<=n;j++){
    if(dis[i][j]>dis[i][k]+dis[k][j]){
     dis[i][j]=dis[i][k]+dis[k][j]; 
    }
   }
  }
 }
 int t=0;
 for(int i=1;i<=n;i++){
  for(int j=1;j<=n;j++){
   if(j!=i)
   t+=dis[i][j]*p[j];
  }
  ans=min(ans,t);
  t=0;
 }
 cout<<ans<<endl;
 return 0;
}

3.强连通分量 - tarjan
这个板子是洛谷2341(奶牛)tarjan缩点,并加了统计出度的部分,出度为0的缩点中奶牛就是最受欢迎的奶牛

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f;
const int maxn=10005;
int head[maxn],dfn[maxn],low[maxn],all[maxn],id[maxn];
bool in[maxn];
int du[maxn];
int n,m,ans,cnt,tot,gg;
struct edge{
 int nxt,to;
} e[maxn*20];
stack<int> s; 
void add(int x,int y){
 e[++cnt].to=y;
 e[cnt].nxt=head[x];
 head[x]=cnt;
}
void tarjan(int x){
 dfn[x]=low[x]=++tot;
 s.push(x);
 in[x]=1;
 for(int i=head[x];i;i=e[i].nxt){
  int u=e[i].to;
  if(!dfn[u]){
   tarjan(u);
   low[x]=min(low[x],low[u]);
  }
  else if(in[u]) low[x]=min(low[x],low[u]);
 }
 int k;
 if(low[x]==dfn[x]){
  gg++;
  do{
   k=s.top();
   s.pop();
   in[k]=0;
   id[k]=gg;
   all[gg]++;
  }
  while(x!=k);
 } 
}
int  main(){
cin>>n>>m;
for(int  i=1;i<=m;i++){
 int a,b;
 cin>>a>>b;
 add(a,b);
}
for(int i=1;i<=n;i++){
 if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
 for(int j=head[i];j;j=e[j].nxt){
  int u=e[j].to;
  if(id[i]!=id[u]) du[id[i]]++;
 }
}
int tt=0;
for(int i=1;i<=gg;i++){
 if(!du[i]){
  if(tt) {
  cout<<0<<endl;
  return 0;
}
  tt=i;
 }
}
cout<<all[tt]<<endl;
 return 0;
}

4.洛谷P1330
染色问题

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
const int maxn=200005;
int n,m,ans;
struct edge{
 int nxt,to;
} e[maxn];
bool use[maxn];
int col[maxn];
int head[20000],cnt;
int sum[2];
//还是链式前向星 
void add(int u,int v){
 e[++cnt].to=v;
 e[cnt].nxt=head[u];
 head[u]=cnt;
}
//dfs 
bool dfs(int node,int c){
//如果节点访问过 判断颜色 
 if(use[node]){
  if(col[node]==c) return 1;
  return 0;
 }
//若未访问过 ,标记颜色,记录个数 
 use[node]=1;
 col[node]=c;
 sum[c]++;
//这个t有何用 不太懂 
bool t=1;
 for(int i=head[node];i;i=e[i].nxt){
  //1-c改变颜色 
  t = t && dfs(e[i].to,1-c);
 }
 return t; 
}
int main(){
 cin >> n >>m;
 for(int i=1;i<=m;i++){
  int a,b;
  cin >> a>>b;
  add(a,b);
  add(b,a);
 }
 for(int i=1;i<=n;i++){
  if(use[i]) continue;
  sum[0]=sum[1]=0;
 //如果已经访问过,代表此点所在的联通块已经扫描完毕 
 //如果没有过,清空数组,扫描此点所在联通块 
   if(!dfs(i,0)){
    cout << "Impossible"<<endl;
    return 0;
   }
   //更新答案,因为是黑白染色,所以两种取最优解即可 
   ans += min(sum[0],sum[1]);
 }
 cout << ans << endl;
 return 0;
}

5.1
洛谷P2661信息传递
判断成环的,也可用并查集

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
int n,ans=0x3f3f3f;
//fir存储的是第一次经过该节点的序号,那么成环时
//大小 = 遍历序号-fir[node] 
int nxt[200005],fir[200005];
//vis是否访问过, forevis:是否已经搜过 
bool vis[200005],forevis[200005];
void dfs(int node,int c){
 if(forevis[node]) return ;
 //成环 
   if(vis[node]){
  ans = min(ans,c-fir[node]);
  return; 
 }
 vis[node]=1;
 fir[node]=c;
 dfs(nxt[node],c+1);
 forevis[node]=1;
}
int main(){
 cin>>n;
 for(int i=1;i<=n;i++)cin>>nxt[i];
 for(int i=1;i<=n;i++){
  dfs(i,0);
 }
 cout << ans << endl;
 return 0;
}

5.2洛谷P2921
与上一题很像,是上一题的补充
不一样的是这里成环
一开始的无脑代码(什么时候能改掉无脑提交的毛病。
显然TLE

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
int n,cnt;
int ans,nxt[100005];
bool vis[100005];
void ini(){
 for(int i=1;i<=n;i++)
 vis[i]=0;
}
void dfs(int id,int t,int s){
 int tmp=nxt[id];
 if(vis[tmp]){
  cout<<t<<endl;
  return ;
 }
 vis[tmp]=1;
 dfs(tmp,t+1,s);
 return;
}
int main(){
 cin>>n;
 for(int i=1;i<=n;i++){
  cin >> nxt[i];
 }
 for(int i=1;i<=n;i++){
  ini();
  vis[i]=1;
  dfs(i,1,i);
 }
 return 0;
}

正解,与上一题有相通之处,强

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
const int maxn=100005;
int n,nxt[maxn],dfn[maxn],cnt;
//vis记录是否访问过,num记录如果成环,环的大小
//time是加入环时走过的步数 
int vis[maxn],num[maxn],t[maxn];
int main(){
 cin >> n;
 for(int i=1;i<=n;i++) cin>>nxt[i];
 for(int i=1;i<=n;i++){
  for(int j=i,cnt=0;;j=nxt[j],cnt++){
   //如果没记录过 
   if(!vis[j]){
    vis[j]=i;
    dfn[j]=cnt;
   }
   //如果正好在环里成环了 
   else if(vis[j]==i){
    num[i]=cnt-dfn[j];
    //时间戳 
    t[i]=dfn[j];
    cout << cnt << endl;
    break;
   }
   //这种情况就是预见到未来的节点会成环
   //但是这个节点之前已经算过了,答案是固定的,so直接计算了
   //节省时间 ,为什么不能记录下到每一个节点的答案呢?
   //一开始我是这样想的,后来发现,就算记录答案,每一次也不确定
   //因为你不知道那头牛在到达这里前,经历了什么...... 
   else{
    //环的大小就是那个点的大小 
    num[i]=num[vis[j]];
    t[i]=cnt+max(0,t[vis[j]]-dfn[j]);
    cout<<t[i]+num[i]<<endl;
    break;
   }
  }
 }
 return 0;
}

6.洛谷P1341无序字母对
欧拉回路:
统计每一个点的度,若都是偶数,则有欧拉回路(有进有出)
若只有两个点的度为奇数,那么以这两个点为起始点有一条欧拉路径
感觉像是一笔画,以前听大佬讲过
代码还比较清楚

#include 
#include
#include
#include
#include 
#include
#include
#include
#include
using namespace std;
int n,fa[300],mp[300][300],du[300],cnt,head,sum;
bool f=0;
char ans[300];
//用并查集判断联通 
int a[300]; 
int find(int x){
 if(fa[x]==x) return x;
 else return fa[x]=find(fa[x]);
}
void dfs(int x){
 for(int i=64;i<=125;i++){
  if(mp[x][i]){
   mp[x][i]=mp[i][x]=0;
   dfs(i);
  }
 }
 ans[++sum]=x;
}
int main(){
 cin >> n;
 for(int i=64;i<=125;i++)fa[i]=i;
 char x[2];
 for(int i=1;i<=n;i++){
  cin >> x;
  //建图加边 
  mp[x[0]][x[1]]=1;
  mp[x[1]][x[0]]=1;
  //欧拉回路 统计度数 
  du[x[0]]++;
  du[x[1]]++;
  fa[find(x[1])]=find(x[0]);
 }
 for(int i=64;i<=125;i++){
  //根 du是为了确定出现过 
  if(fa[i]==i && du[i]) cnt++;
 } 
 if(cnt!=1){
  f=1;
 } 
 cnt=0;
 head=0;
 for(int i=64;i<=125;i++){
  if(du[i]%2){
   cnt++;
   if(head==0) head=i;
  }
 }
 if(cnt&&cnt!=2) f=1;
 if(head==0){
  for(int i=64;i<=125;i++){
   if(du[i]){
    head=i;
    break;
   }
  }
 }
 dfs(head);
 if(f){
  cout<<"No Solution"<<endl;
  return 0;
 }
 else{
  for(int i=n+1;i>=1;i--){
   cout<<ans[i];
  }
  cout<<endl;
 }
 return 0;
}

你可能感兴趣的:(ACM-乱七八糟)