图论
最短路:
//权值非负,不连通为INF、 //O(n^2) void dij(int beg) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[beg] = 0; for(int i = 1;i <= n;i++) { int k = -1,minn = INF; for(int j = 1;j <= n;j++) { if(!vis[j] && dis[j] < minn) { minn = dis[j]; k = j; } } if(k == -1) break; vis[k] = 1; for(int j = 1;j <= n;j++) { if(!vis[j] && dis[k]+a[k][j] < dis[j]) { dis[j] = dis[k]+a[k][j]; } } } }
///O(mlogm) struct xxx { int to,w; xxx(int a,int b):to(a),w(b){}; friend bool operator <(xxx X,xxx Y) { return X.w > Y.w; } }; void dij(int beg) { priority_queue<xxx> q; memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[beg] = 0; q.push(xxx(beg,0)); while(!q.empty()) { int now = q.top().to; q.pop(); if(vis[now]) continue; vis[now] = 1; for(int i = 0;i < v[now].size();i++) { int t = v[now][i].to,w = v[now][i].w; if(!vis[t] && dis[now]+w < dis[t]) { dis[t] = dis[now]+w; q.push(xxx(t,dis[t])); } } } }
//O(n^3) void floyd() { for(int k = 0;k < n;k++) { for(int i = 0;i < n;i++) { for(int j = 0;j < n;j++) a[i][j] = min(a[i][j],a[i][k]+a[k][j]); } } }
//v储存所有边 //O(nm) struct xxx { int u,v,w; xxx(int a,int b,int c):u(a),v(b),w(c){}; }; vector<xxx> v; //存在负环返回0,否则返回1 bool bellman_ford(int beg) { memset(dis,0x3f,sizeof(dis)); dis[beg] = 0; for(int i = 1;i < n;i++) { for(int j = 0;j < v.size();j++) { int x = v[j].u,y = v[j].v,w = v[j].w; dis[y] = min(dis[y],dis[x]+w); } } for(int i = 0;i < v.size();i++) { int x = v[i].u,y = v[i].v,w = v[i].w; if(dis[y] > dis[x]+w) return 0; } return 1; }
//O(km) struct xxx { int to,w; xxx(int a,int b):to(a),w(b){}; }; vector<xxx> v[205]; //存在负环返回0,否则返回1 bool spfa(int beg) { queue<int> q; memset(c,0,sizeof(c)); memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); dis[beg] = 0; q.push(beg); vis[beg] = 1; c[beg] = 1; while(!q.empty()) { int now = q.front(); q.pop(); vis[now] = 0; for(int i = 0;i < v[now].size();i++) { int t = v[now][i].to,w = v[now][i].w; if(dis[now]+w < dis[t]) { dis[t] = dis[now]+w; vis[t] = 1; q.push(t); if(++c[t] > n) return 0; } } } return 1; }
#include<bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; struct xxx { int to,w; xxx(int a,int b):to(a),w(b){}; friend bool operator<(xxx x,xxx y) { return x.w > y.w; } }; vector<xxx> v[11005]; int n,m,k,a[1005][1005],dis[11005],vis[11005]; void dij(int beg) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); priority_queue<xxx> q; dis[beg] = 0; q.push(xxx(beg,0)); while(!q.empty()) { int now = q.top().to; q.pop(); if(vis[now]) continue; vis[now] = 1; for(int i = 0;i < v[now].size();i++) { int t = v[now][i].to,w = v[now][i].w; if(w+dis[now] < dis[t]) { dis[t] = w+dis[now]; q.push(xxx(t,dis[t])); } } } } int main() { while(~scanf("%d%d%d",&n,&m,&k)) { for(int i = 1;i <= 11000;i++) v[i].clear(); memset(a,0x3f,sizeof(a)); for(int i = 1;i <= m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); a[u][v] = min(a[u][v],w); a[v][u] = min(a[v][u],w); } for(int i = 1;i <= n;i++) { for(int j = 1;j <= n;j++) { if(i == j || a[i][j] == INF) continue; for(int l = 0;l <= k;l++) { v[l*n+i].push_back(xxx(l*n+j,a[i][j])); if(l != k) { v[l*n+i].push_back(xxx((l+1)*n+j,0)); } } } } dij(1); printf("%d\n",dis[(k+1)*n]); } return 0; }
最小生成树:
//不连通返回-1,否则返回最小花费 int prim() { int ans = 0; memset(vis,0,sizeof(vis)); vis[1] = 1; for(int i = 2;i <= n;i++) cost[i] = a[1][i]; for(int i = 2;i <= n;i++) { int minn = INF; int k = -1; for(int j = 1;j <= n;j++) { if(!vis[j] && minn > cost[j]) { minn = cost[j]; k = j; } } if(minn == INF) return -1; ans += minn; vis[k] = 1; for(int j = 1;j <= n;j++) { if(!vis[j] && cost[j] > a[k][j]) cost[j] = a[k][j]; } } return ans; }
struct xxx { int to,w; xxx(int a,int b):to(a),w(b){} friend bool operator<(xxx a,xxx b) { return a.w > b.w; } }; //非连通返回-1,否则返回最小花费 int prim() { memset(vis,0,sizeof(vis)); for(int i = 0;i < n;i++) dis[i] = INT_MAX; int ans = 0; priority_queue<xxx> q; q.push(xxx(1,0)); while(!q.empty()) { while(!q.empty() && vis[q.top().to]) q.pop(); if(q.empty()) break; ans += q.top().w; int now = q.top().to; q.pop(); vis[now] = 1; for(int i = 0;i < v[now].size();i++) { int t = v[now][i].to,w = v[now][i].w; if(vis[t]) continue; if(dis[t] <= w) continue; dis[t] = w; q.push(xxx(t,w)); } } return ans; }
struct xxx { int from,to,w; xxx(int a,int b,int c):from(a),to(b),w(c){}; friend bool operator <(xxx a,xxx b) { return a.w < b.w; } }; vector<xxx> v; int findd(int x) { return pre[x] == x?x:findd(pre[x]); } //不连通返回-1,否则返回最小花费 bool kruskal() { for(int i = 1;i <= n;i++) pre[i] = i; int ans = 0,cnt = 0; sort(v.begin(),v.end()); for(int i = 0;i < v.size();i++) { int x = findd(v[i].from),y = findd(v[i].to); if(x != y) { ans += v[i].w; pre[x] = y; cnt++; } } if(cnt < n-1) return -1; return ans; }
矩阵树定理:
1.G的度数矩阵D[G]是一个n*n的矩阵,并且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数。
2.G的邻接矩阵A[G]也是一个n*n的矩阵, 并且满足:如果vi、vj之间有边直接相连,则aij=1,否则为0。
3.G的Kirchhoff矩阵(也称为拉普拉斯算子)C[G]=D[G]-A[G]。
则G的生成树个数等于C[G]任何一个n-1阶主子式的行列式的绝对值。
#include<bits/stdc++.h> using namespace std; int n,m,k,a[55][55]; long long g[55][55]; long long calc() { long long ans = 1; for(int i = 1;i < n;i++) { for(int j = i+1;j < n;j++) { while(g[j][i] != 0) { long long t = g[i][i]/g[j][i]; for(int k = i;k < n;k++) g[i][k] -= t*g[j][k]; for(int k = i;k < n;k++) swap(g[i][k],g[j][k]); ans = -ans; } } if(g[i][i] == 0) return 0; ans = ans*g[i][i]; } return abs(ans); } int main() { ios::sync_with_stdio(false); while(cin >> n >> m >> k) { memset(a,0,sizeof(a)); memset(g,0,sizeof(g)); while(m--) { int x,y; cin >> x >> y; a[x][y] = a[y][x] = 1; } for(int i = 1;i <= n;i++) { int t = 0; for(int j = 1;j <= n;j++) { if(i != j && !a[i][j]) { t++; g[i][j] = -1; } } g[i][i] = t; } cout << calc() << endl; } return 0; }
int n,vis[505],used[505],pre[505],cost[505],a[505][505],maxd[505][505]; //不连通返回-1,否则返回次小花费 int prim() { int ans = 0; memset(vis,0,sizeof(vis)); memset(used,0,sizeof(used)); memset(maxd,0,sizeof(maxd)); vis[1] = 1; for(int i = 2;i <= n;i++) cost[i] = a[1][i]; for(int i = 2;i <= n;i++) { int minn = INF; int k = -1; for(int j = 1;j <= n;j++) { if(!vis[j] && minn > cost[j]) { minn = cost[j]; k = j; } } if(minn == INF) return -1; ans += minn; vis[k] = 1; for(int j = 1;j <= n;j++) { if(vis[j]) maxd[j][k] = maxd[k][j] = max(maxd[j][pre[k]],cost[k]); if(!vis[j] && cost[j] > a[k][j]) { cost[j] = a[k][j]; pre[j] = k; } } } return ans; }
有向图的强联通分量个数:
//O(n+m) //缩点,belong保存每个点属于的强联通集合编号 int n,m,vis[10005],dfn[10005],low[10005],belong[10005],cnt,ans; vector<int> v[10005]; void tarjan(int now) { stack<int> s; s.push(now); vis[now] = 1; dfn[now] = low[now] = ++cnt; for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(dfn[t] == 0) { tarjan(t); low[now] = min(low[now],low[t]); } else if(vis[t]) low[now] = min(low[now],dfn[t]); } if(dfn[now] == low[now]) { ans++; while(!s.empty()) { int t = s.top(); s.pop(); vis[t] = 0; belong[t] = ans; if(t == now) break; } } } int main() { for(int i = 1;i <= n;i++) { if(dfn[i] == 0) tarjan(i); } }
//O(n+m) #include<bits/stdc++.h> using namespace std; int n,m,vis1[10005],vis2[10005],ans; vector<int> v1[10005],v2[10005]; stack<int> s; void dfs1(int now) { vis1[now] = 1; for(int i = 0;i < v1[now].size();i++) { int t = v1[now][i]; if(vis1[t]) continue; dfs1(t); } s.push(now); } void dfs2(int now) { vis2[now] = 1; for(int i = 0;i < v2[now].size();i++) { int t = v2[now][i]; if(vis2[t]) continue; dfs2(t); } } int main() { ios::sync_with_stdio(false); while(scanf("%d%d",&n,&m) &&(n+m)) { for(int i = 1;i <= n;i++) { v1[i].clear(); v2[i].clear(); } memset(vis1,0,sizeof(vis1)); memset(vis2,0,sizeof(vis2)); while(!s.empty()) s.pop(); ans = 0; while(m--) { int x,y; scanf("%d%d",&x,&y); v1[x].push_back(y); v2[y].push_back(x); } for(int i = 1;i <= n;i++) { if(!vis1[i]) dfs1(i); } while(!s.empty()) { if(!vis2[s.top()]) { ans++; dfs2(s.top()); } s.pop(); } if(ans == 1) printf("Yes\n"); else printf("No\n"); } return 0; }
无向图中一些基本概念:
割点集合:删除这个顶点集合,以及这个集合中所有顶点关联的边以后,原图变成多个连通块。
点连通度:最小割点集合中的顶点数。
割边集合:删除这个边集合以后,原图变成多个连通块。
边连通度:最小割边集合中的边数。
点双连通(双连通或重连通):无向连通图的点连通度大于1。
割点(关节点):当且仅当这个图的点连通度为1,则割点集合的唯一元素被称为割点。
边双连通(双连通或重连通):无向连通图的边连通度大于1。
桥(关节边 ):当且仅当这个图的边连通度为 1,则割边集合的唯一元素被称为桥。
边双连通分支:在连通分支中去掉一条边,连通分支仍连通。
点双连通分支(块):在连通分支中去掉一个点,连通分支仍连通。
struct bridge { int u,int v; bridge(int a,int b):u(a),v(b){} }; vector<bridge> ansb; vector<int> absc; //ansb保存桥,ansc保存割点 void tarjan(int now,int pre) { low[now] = dfn[now] = ++cnt; int son = 0; for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(t == pre) continue; if(!dfn[t]) { son++; tarjan(t,now); low[now] = max(low[now],low[t]); //桥,(u,v)为树枝,low[v] > dfn[u] if(low[t] > dfn[now]) { ansb.push_back(bridge(now,t)); ansb.push_back(bridge(t,now)); } //割点,(u,v)父子边,u不为树根,low[v] >= dfn[u] if(now != root && low[t] >= dfn[now]) ansc.push_back(t); } else low[now] = max(low[now],low[t]); } //割点,u为根,分支数大于1 if(now == root && son > 1) ansc.push_back(now); }
去掉桥,其余的连通分支就是边双连通分支了。 一个有桥的连通图要变成双连通图的话,把双连通子图缩成一个点,形成一棵树。 再加上(leaf+1)/2条边。
//cutcnt表示个数 //block储存每个点连通分支 stack<int> s; vector<int> block[10005]; void tarjan(int now,int pre) { low[now] = dfn[now] = ++cnt; s.push(now); for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(t == pre) continue; if(!dfn[t]) { tarjan(t,now); low[now] = max(low[now],low[t]); if(low[t] >= dfn[now]) { cutcnt++; block[cutcnt].clear(); while(!s.empty()) { int tt = s.top(); s.pop(); block[cutcnt].push_back(tt); if(tt = t) break; } block[cutcnt].push_back(now); } } else low[now] = max(low[now],low[t]); } }
struct xxx { int u,v,w; }e[10005]; int n,m,a[55],sum[55],cnt,in[800],pre[800],vis[800],id[800]; void addedge(int u,int v,int w) { e[cnt].u = u; e[cnt].v = v; e[cnt++].w = w; } //已处理重边,不存在最小树形图返回-1,否则返回最小值 int zhuliu(int root, int n, int m) { int ans = 0; while(1) { //找最小入边 for(int i = 0;i < n;i++) in[i] = INF; for(int i = 0; < m;i++) { int u = e[i].u; int v = e[i].v; if(e[i].w < in[v] && u != v) { pre[v] = u; in[v] = e[i].w; } //去除自环和重边 } for(int i = 0;i < n;i++) { if(i == root) continue; if(in[i] == INF) return -1; //除了跟以外有点没有入边,则根无法到达它 } int cnt = 0; //找环 memset(id,-1,sizeof(id)); memset(vis,-1,sizeof(vis)); in[root] = 0; for(int i = 0;i < n;i++) { ans += in[i]; int v = i; while(vis[v] != i && id[v] == -1 && v != root) //每个点寻找其前序点,要么最终寻找至根部,要么找到一个环 { vis[v] = i; v = pre[v]; } if(v != root && id[v] == -1)//缩点 { for(int u = pre[v];u != v;u = pre[u]) id[u] = cnt; id[v] = cnt++; } } if(cnt == 0) break; //无环 则break for(int i = 0;i < n;i++) { if(id[i] == -1) id[i] = cnt++; } for(int i = 0;i < m;) { int u = e[i].u; int v = e[i].v; e[i].u = id[u]; e[i].v = id[v]; if(id[u] != id[v]) e[i++].w -= in[v]; else e[i]=e[--m]; } n = cnt; root = id[root]; } return ans; }
二分图:
最大匹配 == 最小点覆盖
最小路径覆盖 == 最大独立集 == 顶点数-最大匹配
最大团 == 补图最大独立集
int n,m,l; int linker[505]; bool used[505]; int g[505][505]; bool dfs(int u) { for(int v = 1;v <= m;v++) { if(g[u][v] && !used[v]) { used[v] = 1; if(linker[v] == -1 || dfs(linker[v])) { linker[v] = u; return 1; } } } return 0; } int MaxMatch() { int ans = 0; memset(linker,-1,sizeof(linker)); for(int i = 1;i <= n;i++) { memset(used,0,sizeof(used)); if(dfs(i)) ans++; } return ans; }
//O(nl) int n,m,l; int linker[505]; bool used[505]; vector<int> v[505]; bool dfs(int u) { for(int i = 0;i < v[u].size();i++) { int t = v[u][i]; if(!used[t]) { used[t] = 1; if(linker[t] == -1 || dfs(linker[t])) { linker[t] = u; return 1; } } } return 0; } int MaxMatch() { int ans = 0; memset(linker,-1,sizeof(linker)); for(int i = 1;i <= n;i++) { memset(used,0,sizeof(used)); if(dfs(i)) ans++; } return ans; }
//O(V^0.5 E) //适用于数据较大的二分匹配 int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny; int dx[MAXN],dy[MAXN],dis; bool vst[MAXN]; bool searchP() { queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<Nx;i++) if(Mx[i]==-1) { Q.push(i); dx[i]=0; } while(!Q.empty()) { int u=Q.front(); Q.pop(); if(dx[u]>dis) break; for(int v=0;v<Ny;v++) if(g[u][v]&&dy[v]==-1) { dy[v]=dx[u]+1; if(My[v]==-1) dis=dy[v]; else { dx[My[v]]=dy[v]+1; Q.push(My[v]); } } } return dis!=INF; } bool DFS(int u) { for(int v=0;v<Ny;v++) if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1) { vst[v]=1; if(My[v]!=-1&&dy[v]==dis) continue; if(My[v]==-1||DFS(My[v])) { My[v]=u; Mx[u]=v; return 1; } } return 0; } int MaxMatch() { int res=0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(searchP()) { memset(vst,0,sizeof(vst)); for(int i=0;i<Nx;i++) if(Mx[i]==-1&&DFS(i)) res++; } return res; }
bool dfs(int u) { for(int i = 0;i < v[u].size();i++) { int t = v[u][i]; if(used[t]) continue; used[t] = 1; if(linker[t][0] < capacity[t]) { linker[t][++linker[t][0]] = u; return 1; } for(int j = 1;j <= capacity[t];j++) { if(dfs(linker[t][j])) { linker[t][j] = u; return 1; } } } return 0; } int MaxMatch() { int ans = 0; memset(linker,0,sizeof(linker)); for(int i = 1;i <= n;i++) { memset(used,0,sizeof(used)); if(dfs(i)) ans++; } return ans; }
int n,m,g[305][305],linker[305],lx[305],ly[305],slack[305],visx[305],visy[305]; bool dfs(int u) { visx[u] = 1; for(int i = 1;i <= m;i++) { if(visy[i]) continue; int t = lx[u]+ly[i]-g[u][i]; if(t == 0) { visy[i] = 1; if(!linker[i]|| dfs(linker[i])) { linker[i] = u; return 1; } } else slack[i] = min(slack[i],t); } return 0; } int KM() { memset(linker,0,sizeof(linker)); memset(ly,0,sizeof(ly)); for(int i = 1;i <= n;i++) { lx[i] = -INF; for(int j = 1;j <= m;j++) { if(g[i][j] > lx[i]) lx[i] = g[i][j]; } } for(int i = 1;i <= n;i++) { memset(slack,0x3f,sizeof(slack)); while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(dfs(i)) break; int d = INF; for(int j = 1;j <= m;j++) { if(!visy[j]) d = min(d,slack[j]); } for(int j = 1;j <= n;j++) { if(visx[j]) lx[j] -= d; } for(int j = 1;j <= m;j++) { if(visy[j]) ly[j] += d; else slack[j] -= d; } } } int ans = 0; for(int i = 1;i <= m;i++) { if(linker[i]) ans += g[linker[i]][i]; } return ans; }
网络流:
最大流 == 最小割
//s为源点,t为汇点 //O(nm^2) int n,m,s,t,a[20][20],pre[20],vis[20]; bool bfs() { memset(pre,0,sizeof(pre)); memset(vis,0,sizeof(vis)); vis[s] = 1; queue<int> q; q.push(s); while(!q.empty()) { int now = q.front(); q.pop(); if(now == t) return 1; for(int i = 1;i <= n;i++) { if(vis[i]) continue; if(a[now][i]) { q.push(i); pre[i] = now; vis[i] = 1; } } } return 0; } int EdmondsKarp() { int ans = 0; while(bfs()) { int minn = 1e9; for(int i = t;i != s;i = pre[i]) minn = min(minn,a[pre[i]][i]); for(int i = t;i != s;i = pre[i]) { a[pre[i]][i] -= minn; a[i][pre[i]] += minn; } ans += minn; } return ans; }
struct edge { int u,v,c,next; }e[20*N]; int n,m,sp,tp,cnt,head[N],d[N]; //源点汇点全局定义 void init() { cnt = 0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c) { e[cnt].u = u;e[cnt].v = v;e[cnt].c = c; e[cnt].next = head[u];head[u] = cnt++; e[cnt].u = v;e[cnt].v = u;e[cnt].c = 0; e[cnt].next = head[v];head[v] = cnt++; } int bfs() { queue<int> q; memset(d,-1,sizeof(d)); d[sp]=0; q.push(sp); while(!q.empty()) { int cur = q.front(); q.pop(); for(int i = head[cur];i != -1;i = e[i].next) { int u = e[i].v; if(d[u] == -1 && e[i].c > 0) { d[u] = d[cur]+1; q.push(u); } } } return d[tp] != -1; } int dfs(int a,int b) { int r = 0; if(a == tp) return b; for(int i = head[a];i != -1 && r < b;i = e[i].next) { int u = e[i].v; if(e[i].c > 0 && d[u] == d[a]+1) { int x = min(e[i].c,b-r); x = dfs(u,x); r += x; e[i].c -= x; e[i^1].c += x; } } if(!r) d[a] =- 2; return r; } int dinic() { int ans = 0,t; while(bfs()) { while(t = dfs(sp,INF)) ans += t; } return ans; }
struct Edge { int to,next,cap,flow; } edge[M]; int n,m,cnt,head[N],gap[N],d[N],cur[N],que[N],p[N]; void init() { cnt = 0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c) { edge[cnt].to = v; edge[cnt].cap = c; edge[cnt].flow = 0; edge[cnt].next = head[u]; head[u] = cnt++; edge[cnt].to = u; edge[cnt].cap = 0; edge[cnt].flow = 0; edge[cnt].next = head[v]; head[v] = cnt++; } void bfs(int source,int sink) { memset(d,-1,sizeof(d)); memset(gap,0,sizeof(gap)); gap[0] = 1; int front = 0,rear = 0; d[sink] = 0; que[rear++] = sink; while(front != rear) { int u = que[front++]; for(int i = head[u];i != -1;i = edge[i].next) { int v = edge[i].to; if(d[v] != -1) continue; que[rear++] = v; d[v] = d[u]+1; gap[d[v]]++; } } } int isap(int source,int sink,int N) //源点,汇点,总点数 { bfs(source,sink); memcpy(cur,head,sizeof(head)); int top = 0,x = source,flow = 0; while(d[source] < N) { if(x == sink) { int Min = INF,inser; for(int i = 0;i < top;i++) { if(Min > edge[p[i]].cap-edge[p[i]].flow) { Min = edge[p[i]].cap-edge[p[i]].flow; inser = i; } } for(int i = 0;i < top;i++) { edge[p[i]].flow += Min; edge[p[i]^1].flow -= Min; } flow += Min; top = inser; x = edge[p[top]^1].to; continue; } int ok = 0; for(int i = cur[x];i != -1;i = edge[i].next) { int v = edge[i].to; if(edge[i].cap > edge[i].flow && d[v]+1 == d[x]) { ok = 1; cur[x] = i; p[top++] = i; x = edge[i].to; break; } } if(!ok) { int Min = N; for(int i = head[x];i != -1;i = edge[i].next) { if(edge[i].cap > edge[i].flow && d[edge[i].to] < Min) { Min = d[edge[i].to]; cur[x] = i; } } if(--gap[d[x]] == 0) break; gap[d[x] = Min+1]++; if(x != source) x = edge[p[--top]^1].to; } } return flow; }
int n,m,s,t,pre[1005],vis[1005],dis[1005],head[1005],cnt = 0; struct ee { int u,v,cap,cost,next; }e[2005]; void add(int u,int v,int cap,int cost) { e[cnt].u = u; e[cnt].v = v; e[cnt].cap = cap; e[cnt].cost = cost; e[cnt].next = head[u]; head[u] = cnt++; e[cnt].u = v; e[cnt].v = u; e[cnt].cap = 0; e[cnt].cost = -cost; e[cnt].next = head[v]; head[v] = cnt++; } bool spfa() { memset(vis,0,sizeof(vis)); memset(pre,-1,sizeof(pre)); for(int i = s;i <= t;i++) dis[i] = 1e9; vis[s] = 1; dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u];i != -1;i = e[i].next) { int v = e[i].v; if(e[i].cap && dis[v] > dis[u]+e[i].cost) { dis[v] = dis[u]+e[i].cost; pre[v] = i; if(vis[v]) continue; q.push(v); vis[v] = 1; } } } if(dis[t] == 1e9) return 0; return 1; } int mcmf() { int flow = 0,ans = 0; while(spfa()) { int minn = 1e9; for(int i = pre[t];i != -1;i = pre[e[i].u]) minn = min(e[i].cap,minn); for(int i = pre[t];i != -1;i = pre[e[i].u]) { e[i].cap -= minn; e[i^1].cap += minn; } flow += minn; ans += dis[t]*minn; } return ans; }
2-sat:
x == 1 : x' -> x (x必取)
x and y == 1 : x' -> x ,y' -> y (两个数必须全为1)
x and y == 0 : y -> x' ,x -> y' (两个数至少有一个为0)
x or y == 1 : x' -> y ,y' -> x (两个数至少有一个为1)
x or y == 0 : x -> x' ,y -> y' (两个数全为0)
x xor y == 1 : x -> y' ,y -> x' ,y' -> x ,x '-> y (两个数不同)
x xor y == 0 : x -> y ,x' -> y' ,y -> x ,y' -> x' (两个数相同)
//可得到最小字典序的解 #include<bits/stdc++.h> using namespace std; int n,m,vis[16005]; vector<int> v[16005]; stack<int> s; bool dfs(int u) { if(vis[u^1]) return 0; if(vis[u]) return 1; vis[u] = 1; s.push(u); for(int i = 0;i < v[u].size();i++) { int t = v[u][i]; if(!dfs(t)) return 0; } return 1; } bool twosat(int n) { memset(vis,0,sizeof(vis)); for(int i = 0;i < n;i += 2) { if(vis[i] || vis[i^1]) continue; while(!s.empty()) s.pop(); if(!dfs(i)) { while(!s.empty()) { vis[s.top()] = 0; s.pop(); } if(!dfs(i^1)) return 0; } } return 1; } int main() { ios::sync_with_stdio(false); while(cin >> n >> m) { for(int i = 0;i < 2*n;i++) v[i].clear(); memset(vis,0,sizeof(vis)); while(m--) { int a,b; cin >> a >> b; a--; b--; v[a].push_back(b^1); v[b].push_back(a^1); } if(twosat(2*n)) { for(int i = 0;i < 2*n;i++) { if(vis[i]) cout << i+1 << endl; } } else cout << "NIE" << endl; } return 0; }
int n,m,vis[2*N],dfn[2*N],low[2*N],belong[2*N],pos[2*N],deg[2*N],ok[2*N],cnt,color; vector<int> v[2*N],v2[2*N],ans; stack<int> st; //求任意解 void top(int n) { ans.clear(); memset(ok,0,sizeof(ok)); queue<int> q; for(int i = 1;i <= color;i++) { if(deg[i] == 0) q.push(i); } while(!q.empty()) { int now = q.front(); q.pop(); if(ok[now] == 0) { ok[now] = 1; ok[pos[now]] = 2; } for(int i = 0;i < v2[now].size();i++) { int t = v2[now][i]; if(--deg[t] == 0) q.push(t); } } for(int i = 0;i < n;i++) { if(ok[belong[i]] == 1) ans.push_back(i); } } void tarjan(int now) { st.push(now); vis[now] = 1; dfn[now] = low[now] = ++cnt; for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(dfn[t] == 0) { tarjan(t); low[now] = min(low[now],low[t]); } else if(vis[t]) low[now] = min(low[now],dfn[t]); } if(dfn[now] == low[now]) { color++; while(!st.empty()) { int t = st.top(); st.pop(); vis[t] = 0; belong[t] = color; if(t == now) break; } } } bool solve(int n) { for(int i = 0;i < n;i++) { if(dfn[i] == 0) tarjan(i); } for(int i = 0;i < n;i += 2) { if(belong[i] == belong[i^1]) return 0; pos[belong[i]] = belong[i^1]; pos[belong[i^1]] = belong[i]; } for(int i = 0;i < n;i++) { for(int j = 0;j < v[i].size();j++) { int t = v[i][j]; if(belong[i] != belong[t]) { deg[belong[i]]++; v2[belong[t]].push_back(belong[i]); } } } top(n); return 1; }
struct point { int x,y,id; friend bool operator <(point a,point b) { if(a.x != b.x) return a.x < b.x; return a.y < b.y; } }p[10005]; struct Bit { int minn,pos; void init() { minn = INF; pos = -1; } }bit[10005]; struct edge { int u,v,d; friend bool operator<(edge a,edge b) { return a.d < b.d; } }e[40005]; int n,k,cnt,pre[10005],a[10005],b[10005]; int findd(int x) { return pre[x] == x?x:findd(pre[x]); } void addedge(int u,int v,int d) { e[++cnt].u = u; e[cnt].v = v; e[cnt].d = d; } int lowbit(int x) { return x&-x; } void update(int i,int x,int pos) { while(i > 0) { if(x < bit[i].minn) { bit[i].minn = x; bit[i].pos = pos; } i -= lowbit(i); } } int ask(int i,int m) { int minn = INF,pos = -1; while(i <= m) { if(bit[i].minn < minn) { minn = bit[i].minn; pos = bit[i].pos; } i += lowbit(i); } return pos; } int dist(point a,point b) { return abs(a.x-b.x)+abs(a.y-b.y); } void manhattan() { cnt = 0; for(int dir = 0;dir < 4;dir++) { if(dir == 1 || dir == 3) { for(int i = 1;i <= n;i++) swap(p[i].x,p[i].y); } else if(dir == 2) { for(int i = 1;i <= n;i++) p[i].x = -p[i].x; } sort(p+1,p+1+n); for(int i = 1;i <= n;i++) a[i] = b[i] = p[i].y-p[i].x; sort(b+1,b+1+n); int t = unique(b+1,b+n+1)-b-1; for(int i = 1;i <= t;i++) bit[i].init(); for(int i = n;i >= 1;i--) { int pos = lower_bound(b+1,b+1+t,a[i])-b+1; int ans = ask(pos,t); if(ans != -1) addedge(p[i].id,p[ans].id,dist(p[i],p[ans])); update(pos,p[i].x+p[i].y,i); } } } //返回第k小的边长 int solve(int k) { manhattan(); for(int i = 1;i <= n;i++) pre[i] = i; sort(e+1,e+1+cnt); for(int i = 1;i <= cnt;i++) { int u = e[i].u,v = e[i].v; int x = findd(u),y = findd(v); if(x != y) { pre[x] = y; if(--k == 0) return e[i].d; } } }
//g[i][j]存放关系图:i,j是否有边,match[i]存放i所匹配的点 //建图开始初始化g //最终匹配方案为match //复杂度O(n^3) //点是从1到n的 bool g[MAXN][MAXN],inque[MAXN],inblossom[MAXN],inpath[MAXN]; int match[MAXN],pre[MAXN],base[MAXN]; queue<int> q; //找公共祖先 int findancestor(int u,int v) { memset(inpath,false,sizeof(inpath)); while(1) { u = base[u]; inpath[u] = 1; if(match[u] == -1) break; u = pre[match[u]]; } while(1) { v = base[v]; if(inpath[v]) return v; v = pre[match[v]]; } } //压缩花 void reset(int u,int anc) { while(u != anc) { int v = match[u]; inblossom[base[u]] = 1; inblossom[base[v]] = 1; v = pre[v]; if(base[v] != anc) pre[v] = match[u]; u = v; } } void contract(int u,int v,int n) { int anc = findancestor(u,v); memset(inblossom,0,sizeof(inblossom)); reset(u,anc); reset(v,anc); if(base[u] != anc) pre[u] = v; if(base[v] != anc) pre[v] = u; for(int i = 1;i <= n;i++) { if(inblossom[base[i]]) { base[i] = anc; if(!inque[i]) { q.push_back(i); inque[i] = 1; } } } } bool bfs(int S,int n) { for(int i = 0;i <= n;i++) pre[i]=-1,inque[i]=0,base[i]=i; q.clear(); q.push_back(S); inque[S] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int v = 1;v <= n;v++) { if(g[u][v] && base[v] != base[u] && match[u] != v) { if(v == S || match[v] !=-1 && pre[match[v]] != -1) contract(u,v,n); else if(pre[v] == -1) { pre[v] = u; if(match[v] != -1) { q.push_back(match[v]); inque[match[v]] = 1; } else { u = v; while(u! = -1) { v = pre[u]; int w = match[v]; match[u] = v; match[v] = u; u = w; } return 1; } } } } } return 0; } int solve(int n) { memset(match,-1,sizeof(match)); int ans = 0; for(int i=1;i<=n;i++) { if(match[i] == -1 && dfs(i,n)) ans++; } return ans; }
void dfs(int now,int pre) { dep[now] = dep[pre]+1; for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(t == pre) continue; fa[t][0] = now; dfs(t,now); } } int lca(int x,int y) { if(dep[x] < dep[y]) swap(x,y); int t = dep[x]-dep[y]; for(int i = 0;i <= 20;i++) { if((1<<i)&t) x = fa[x][i]; } if(x == y) return x; for(int i = 20;i >= 0;i--) { if(fa[x][i] != fa[y][i]) { x = fa[x][i]; y = fa[y][i]; } } return fa[x][0]; } int main() { dfs(1,0); for(int i = 1;i <= 20;i++) { for(int j = 1;j <= n;j++) { fa[j][i] = fa[fa[j][i-1]][i-1]; } } }
int n,m,f[N*2],rmq[N*2]; vector<int> v[N]; struct ST { int mm[2*N],dp[2*N][20]; void init(int n) { mm[0] = -1; for(int i = 1;i <= n;i++) { mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1]; dp[i][0] = i; } for(int j = 1;j <= mm[n];j++) { for(int i = 1;i+(1<<j)-1 <= n;i++) dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1]; } } int query(int a,int b) { if(a > b) swap(a,b); int k = mm[b-a+1]; return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k]; } }st; void dfs(int now,int pre,int dep) { f[++cnt] = now; rmq[cnt] = dep; p[now] = cnt; for(int i = 0;i < v[now].size();i++) { int t = v[now][i]; if(t == pre) continue; dfs(t,now,dep+1); f[++cnt] = now; rmq[cnt] = dep; } } void initlca(int root,int n) { cnt = 0; dfs(root,root,0); st.init(2*n-1); } int querylca(int x,int y) { return f[st.query(p[x],p[y])]; }
欧拉路径:
①无向图:连通,每个顶点度数都为偶数或者仅有两个点的度数为奇数。
②有向图:底图连通,所有顶点的出度 == 入度 或者 有且仅有一个顶点 出度 == 入度+1,有且仅有一个顶点 入度 == 出度+1。
③混合图:底图连通,存在欧拉回路,则一定存在欧拉路径。否则如果有且仅有两个点的(出度-入度)是奇数,那么给这两个点加边,判断是否存在欧拉回路。
欧拉回路:
①无向图:连通,每个顶点度数都为偶数。
②有向图:底图连通,每个顶点出度等于入度。
③混合图:底图连通,网络流判断。
vector<int> ans; struct edge { int to,next,id,dir,flag; }e[20005]; void addedge(int u,int v,int id) { e[cnt].to = v; e[cnt].id = id; e[cnt].dir = 0; e[cnt].flag = 0; e[cnt].next = head[u]; head[u] = cnt++; e[cnt].to = u; e[cnt].id = id; e[cnt].dir = 1; e[cnt].flag = 0; e[cnt].next = head[v]; head[v] = cnt++; } vector<edge> ans; void dfs(int u) { for(int i = head[u];i != -1;i = e[i].next) { if(!e[i].flag) { e[i].flag = 1; e[i^1].flag = 0; dfs(e[i].to); ans.push_back(i); } } }
#define MAXN 250 vector<int> ans; struct edge { int to,next,id,flag; }e[20005]; void addedge(int u,int v,int id) { e[cnt].to = v; e[cnt].id = id; e[cnt].flag = 0; e[cnt].next = head[u]; head[u] = cnt++; } vector<edge> ans; void dfs(int u) { for(int i = head[u];i != -1;i = e[i].next) { if(!e[i].flag) { e[i].flag = 1; dfs(e[i].to); ans.push_back(i); } } }
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<queue> #define INF 0x3f3f3f3f #define N 205 using namespace std; struct edge { int u,v,cap,next; }e[20005]; int n,m,cnt,head[N],dep[N],gap[N],cur[N],st[N],que[N],ind[N],outd[N]; void addedge(int u,int v,int cap) { e[cnt].u = u; e[cnt].v = v; e[cnt].cap = cap; e[cnt].next = head[u]; head[u] = cnt++; e[cnt].u = v; e[cnt].v = u; e[cnt].cap = 0; e[cnt].next = head[v]; head[v] = cnt++; } void bfs(int des) { memset(dep,-1,sizeof(dep)); memset(gap,0,sizeof(gap)); gap[0] = 1; int front = 0, rear = 0; dep[des] = 0; que[rear++] = des; int u,v; while(front != rear) { u = que[front++]; front %= N; for(int i = head[u];i != -1;i = e[i].next) { v = e[i].v; if(e[i].cap != 0 || dep[v] != -1) continue; que[rear++] = v; rear %= N; ++gap[dep[v] = dep[u]+1]; } } } int isap(int src,int des,int n)//源点、汇点、图中点的总数 { bfs(des); int ans = 0,top = 0; memcpy(cur,head,sizeof(head)); int u = src,i; while(dep[src] < n) { if(u == des) { int temp = INF,inser = n; for(i = 0;i != top;i++) { if (temp > e[st[i]].cap) { temp = e[st[i]].cap; inser = i; } } for(i = 0;i != top;i++) { e[st[i]].cap -= temp; e[st[i]^1].cap += temp; } ans += temp; top = inser; u = e[st[top]].u; } if(u != des && !gap[dep[u]-1]) break; for(i = cur[u];i != -1;i = e[i].next) { if(e[i].cap != 0 && dep[u] == dep[e[i].v]+1) break; } if(i != -1) { cur[u] = i; st[top++] = i; u = e[i].v; } else { int minn = n; for(i = head[u];i != -1;i = e[i].next) { if(!e[i].cap) continue; if(minn > dep[e[i].v]) { minn = dep[e[i].v]; cur[u] = i; } } --gap[dep[u]]; ++gap[dep[u] = minn+1]; if(u != src) u = e[st[--top]].u; } } return ans; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); cnt = 0; memset(head,-1,sizeof(head)); memset(ind,0,sizeof(ind)); memset(outd,0,sizeof(outd)); while(m--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); outd[x]++; ind[y]++; if(z == 0) addedge(x,y,1); } int sum = 0,flag = 1; for(int i = 1;i <= n;i++) { if(outd[i]-ind[i] > 0) { addedge(0,i,(outd[i]-ind[i])/2); sum += (outd[i]-ind[i])/2; } else if(ind[i]-outd[i] > 0) addedge(i,n+1,(ind[i]-outd[i])/2); if((outd[i]-ind[i])%2) flag = 0; } if(flag && isap(0,n+1,n+2) == sum) printf("possible\n"); else printf("impossible\n"); } return 0; }