【图论】【刷题】【蓝绿题】【强连通】【拓扑】
1>神经网络
绿题
为什么用拓扑?
1)必备条件:DAG
2)本题的公式上,显示需要计算出能连接到本点的,全部的边值*点值
故而当我开始推这个点时,他的所有先驱点的状态全部都要求完
这只能是topo_sort
#include<cstdio> #include<cstdlib> #include<queue> #include<vector> using namespace std; int n,m; const int N=103; int c[N],in[N],sz[N]; struct node { int v,w; node(int vv,int ww) { v=vv,w=ww; } node(){} }; vector <node > g[N]; queue <int > q; void topo_sort() { while(!q.empty()) { int t=q.front();q.pop(); sz[t]=g[t].size() ; for(int i=0;i<sz[t];i++) { node v=g[t][i]; if(c[t]>0) c[v.v ]+=c[t]*v.w ; if(--in[v.v ]==0) q.push(v.v ); } } } int main() { scanf("%d%d",&n,&m); int x; for(int i=1;i<=n;i++) { scanf("%d%d",&c[i],&x); if(c[i]>0) q.push(i); else c[i]-=x; } int u,v,w; while(m--) { scanf("%d%d%d",&u,&v,&w); g[u].push_back(node(v,w)); in[v]++; } topo_sort(); bool fail=true; for(int i=1;i<=n;i++) if(!sz[i] && c[i]>0) { printf("%d %d\n",i,c[i]); fail=false; } if(fail) printf("NULL\n"); return 0; }
2>校园网络
>难点:
DAG中,如何加入最少的边,使图中所有边都在一个强连通分量中
>试着从in,out的方面分析问题,
加边:入度+1,出度+1
>推测 :
缩点 后的任意有向图
若要向其中添加一些边,使得仅存一个强连通分量,
添加边的数目min= max(入度为0的点 , 出度为0的点)
>证明:(luogu)
一个任意DAG,必定是由多个链形成的,必然有起点u和终点v
我们先建立u->v的边,嗯链少了一条,变成了点,
入度为0的点-1,出度为0的点-1
重复以上步骤,
直到最后所有的入度为0的点,出度为0的点,都消失了,
再看看图,就没有链了,都是环,或者说最后只剩下一个强连通分量
#include<cstdio> #include<cstdlib> #include<vector> #include<stack> #include<algorithm> using namespace std; int n,sum; const int N=10003; vector <int > g[N]; int fa[N],sz[N],dfn[N],low[N],tt; stack <int> s; bool f[N]; void tarjan (int x) { dfn[x]=low[x]=++tt; f[x]=true,s.push(x); sz[x]=g[x].size() ; for(int i=0;i<sz[x];i++) { int v=g[x][i]; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(f[v]) low[x]=min(low[x],low[v]); } if(dfn[x]==low[x]) { ++sum; while(s.top()!=x) f[s.top()]=false,fa[s.top()]=sum,s.pop(); f[x]=false,fa[x]=sum,s.pop() ; } } int in[N],out[N];//存sum节点的入度 int main() { scanf("%d",&n); int x; for(int i=1;i<=n;i++) while(~scanf("%d",&x) && x) g[i].push_back(x); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) { int fu=fa[i],fv; for(int j=0;j<sz[i];j++) { int v=g[i][j]; fv=fa[v]; if(fu!=fv) in[fv]++,out[fu]++; } } int ans1=0,ans2=0; for(int i=1;i<=sum;i++) { if(!in[i]) ans1++; if(!out[i]) ans2++; } printf("%d\n%d",ans1,max(ans1,ans2)); return 0; }
3>旅行计划
这道题的题面很有意思啊
所有城市之所以能走,有两个条件
1)u在v的西面
2)u和v之间有一条边
所以其实这个拓扑序是真的很明显了,
所求出的拓扑序,其实是东到西的一条街啊
很巧的有以下性质:
1)无环,不强连通
2)无后效性,我到u城市走的是最远的路,再到v城市,就不会再遇到前面的城市
多好的topo_sort啊
好水啊
#include<cstdio> #include<cstdlib> #include<queue> #include<vector> using namespace std; int n,m; const int N=100003; vector <int> g[N]; int in[N],ans[N]; queue <int> q ; void topo_sort() { for(int i=1;i<=n;i++) if(!in[i]) q.push(i); while(!q.empty() ) { int u=q.front() ;q.pop() ; int sz=g[u].size() ; for(int i=0;i<sz;i++) { int v=g[u][i]; ans[v]=ans[u]+1;//是总起点开始的,所以这个有点像bfs,步数再增加或者不变 if(--in[v]==0) q.push(v); } } for(int i=1;i<=n;i++) printf("%d\n",ans[i]+1); } int main() { scanf("%d%d",&n,&m); int u,v; while(m--) { scanf("%d%d",&u,&v); g[u].push_back(v); in[v]++; } topo_sort(); return 0; }
4>排序
给你n个字母,m个大小限制,
求出正确的排序
附加要求:
1)判断矛盾关系,输出矛盾出现的条件序号(就是第一个错误)
2)判断准确的大小关系,如果没有只能输出另一种结果!
所以我们要实现
1)在线判环。
最多是一颗生成树啊,25条边,
破圈法也行,
不过我用的是状压+floyd,也不知道是哪来的想法,大概是最近搜索剪枝写多了
using namespace std; char s[10]; int n,m,cnt; const int N=30; bitset <N> bs[N],in[N]; bool vis[N];
void floyd(int x) { for(int i=1;i<=n;i++) if(bs[i][x] && (bs[i]|bs[x])!=bs[i]) bs[i]|=bs[x],floyd(i); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%s",s); int u=s[0]-'A'+1,v=s[2]-'A'+1; if(bs[v][u] || u==v) { printf("Inconsistency found after %d relations.",i); return 0; } bs[u][v]=1; floyd(v); in[v][u]=1; if(cnt<n) { if(!vis[u]) vis[u]=true,cnt++; if(!vis[v]) vis[v]=true,cnt++;; } if(cnt==n) topo_sort(i); } printf("Sorted sequence cannot be determined."); return 0; }
我代码中,开始拓扑排序有两个条件
1)每个点都被边涉及到,不是指出,就是指入
用到了一个cnt,一个vis数组
2)没有回边,floyd在线更新(这里要剪枝,只有这个点变了,才会继续往上走)
这样就解决了矛盾的问题
然后讨论另外两种情况
1)边够了,topo却不能确定准确顺序
思考这样会出现什么情况:
就是topo中出现两个深度相同的点,
就是一对兄弟,都知道是爸妈生的,但是不知道谁先出生,
这样就出现了两个nx,return 但不要结束程序
2)出现了准确的排序
打印,exit(0),完成
bitset <N> tt; int sum,d[N],inn[N]; void topo_sort(int step) { tt.reset(); for(int i=1;i<=n;i++) tt|=bs[i]; sum=n-tt.count(); if(sum!=1) return ; for(int i=1;i<=n;i++) inn[i]=in[i].count(); int nw,nx; for(nw=1;nw<=n;nw++) if(!tt[nw]) break; d[sum=1]=nw; while(sum<n) { nx=0; for(int i=1;i<=n;i++) { if(!bs[nw][i]) continue; if(--inn[i]==0) { if(!nx) nx=i; else return ; } } nw=nx; d[++sum]=nw; } printf("Sorted sequence determined after %d relations: ",step); for(int i=1;i<=n;i++) printf("%c",d[i]+'A'-1); printf("."); exit(0); }
最后再加上当所有边都处理了,还不能有序或矛盾,就输出不确定
完整版:
#include<cstdio> #include<cstdlib> #include<bitset> #include<cstring> using namespace std; char s[10]; int n,m,cnt; const int N=30; bitset <N> bs[N],in[N]; bool vis[N]; bitset <N> tt; int sum,d[N],inn[N]; void topo_sort(int step) { tt.reset(); for(int i=1;i<=n;i++) tt|=bs[i]; sum=n-tt.count(); if(sum!=1) return ; for(int i=1;i<=n;i++) inn[i]=in[i].count(); int nw,nx; for(nw=1;nw<=n;nw++) if(!tt[nw]) break; d[sum=1]=nw; while(sum<n) { nx=0; for(int i=1;i<=n;i++) { if(!bs[nw][i]) continue; if(--inn[i]==0) { if(!nx) nx=i; else return ; } } nw=nx; d[++sum]=nw; } printf("Sorted sequence determined after %d relations: ",step); for(int i=1;i<=n;i++) printf("%c",d[i]+'A'-1); printf("."); exit(0); } void floyd(int x) { for(int i=1;i<=n;i++) if(bs[i][x] && (bs[i]|bs[x])!=bs[i]) bs[i]|=bs[x],floyd(i); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%s",s); int u=s[0]-'A'+1,v=s[2]-'A'+1; if(bs[v][u] || u==v) { printf("Inconsistency found after %d relations.",i); return 0; } bs[u][v]=1; floyd(v); in[v][u]=1; if(cnt<n) { if(!vis[u]) vis[u]=true,cnt++; if(!vis[v]) vis[v]=true,cnt++;; } if(cnt==n) topo_sort(i); } printf("Sorted sequence cannot be determined."); return 0; }