防2B && 图论模板 && 7788
1.二分图判定染色模板
bool dfs(int u, int cur){ col[u]=cur; for(int i=0; i<g[u].size(); i++){ int v=g[u][i]; if(col[u]==col[v] || (!col[v] && !dfs(v, 3-cur))) return false; } return true; }
2.floyd模板
void floyd(){ //求可达性 int k, i, j; for(k=1; k<=n; k++) for(i=1; i<=n; i++) for(j=1; j<=n; j++) if(!reach[i][j]) reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]); }
3.tarjan 求 割边 / 桥
void dfs(int u){ dfn[u]=low[u]=++tsp; vis[u]=ins[u]=true; s.push(u); for(int i=h[u]; i!=-1; i=e[i].nxt){ int v=e[i].v; if(!vis[v]){ ve[e[i].re]=true; dfs(v), low[u]=MIN(low[v], low[u]); if(dfn[v]==low[v]) ans=MIN(ans, e[i].w); //割点,割边 } else if(ins[v] && !ve[i]){ ve[e[i].re]=true; low[u]=MIN(dfn[v], low[u]); } } if(dfn[u]==low[u]){ //割点 int v; do{ v=s.top(); s.pop(); ins[v]=false; }while(v!=u); } } void tarjan(){ int i, j; memset(vis, 0, sizeof(vis)); memset(ve, 0, sizeof(ve)); memset(ins, 0, sizeof(ins)); int f=tsp=0; for(i=1; i<=n; i++) if(!vis[i]) dfs(i), f++; if(f>1) ans=0; if(ans==INF) ans=-1; }
4.tarjan 求强连通分量
void tarjan(int u){ dfn[u]=low[u]=++tsp; s.push(u); ins[u]=true; int i, v, tl=g[u].size(); for(i=0; i<tl; i++){ v=g[u][i]; if(!dfn[v]) tarjan(v), low[u]=MIN(low[u], low[v]); else if(ins[v]) low[u]=MIN(low[u], dfn[v]); //ins避免横叉边 } if(low[u]==dfn[u]){ //将强连通分量缩为一点 cnt++; num[cnt]=0; // cout<<cnt<<endl; do{ v=s.top(); s.pop(); // cout<<' '<<v; id[v]=cnt; ins[v]=false; num[cnt]++; }while(v!=u); // cout<<"\tnumber:"<<num[cnt]<<endl; } }
5.tarjan 求点双连通分量
void tarjan(int u, int fa){ int v, child=0; dfn[u]=low[u]=++tsp; for(unsigned int i=0; i<g[u].size(); i++){ v=g[u][i]; edge t; t.u=u; t.v=v; if(!dfn[v]){ child++; s.push(t); tarjan(v, u); low[u]=MIN(low[u], low[v]); if(low[v]>=dfn[u]){ iscut[u]=1; edge k; bcc[++cnt].clear(); while(1){ k=s.top(); s.pop(); if(id[k.u] != cnt) {id[k.u]=cnt; bcc[cnt].push_back(k.u);} if(id[k.v] != cnt) {id[k.v]=cnt; bcc[cnt].push_back(k.v);} if(k.u==u && k.v==v) break; } } } else if(dfn[v]<dfn[u] && v!=fa) s.push(t), low[u]=MIN(low[u], dfn[v]); } if(fa<0 && child==1) iscut[u]=0; }
6.tarjan 边双连通分量
void tarjan(int u){ int i,v; dfn[u]=low[u]=++tsp; s[top++]=u; for(i=h[u]; i!=-1; i=e[i].nxt){ v=e[i].v; if(!dfn[v]){ vis[e[i].re]=1; tarjan(v); low[u]=MIN(low[u], low[v]); } else if(!vis[i]) low[u]=MIN(low[u], dfn[v]); //回边不是反向边,且该点:dfn[v] == low[v] } if(low[u] == dfn[u]){ cnt++; do{ v=s[--top]; id[v]=cnt; //缩点 }while(v!=u); } }
7.稳定婚姻问题 / propose-and-reject algorithm
void engage(int man, int woman){ int m=fb[woman]; if(m) q.push(m), fw[m]=0; //如果有未婚夫,抛弃 fw[man]=woman; fb[woman]=man; } void solve(){ memset(fw, 0, sizeof(fw)); memset(fb, 0, sizeof(fb)); while(!q.empty()){ int man=q.front(); q.pop(); int woman=pref[man][nxt[man]++]; if(!fb[woman]) engage(man, woman); //没有未婚夫 else if(order[woman][fb[woman]]>order[woman][man]) engage(man, woman); //出现更迷人的汉子 else q.push(man); } for(int i=1; i<=n; i++) cout<<manname[i]<<' '<<womanname[fw[i]]<<endl; }
(详细见这里:http://www.cnblogs.com/ramanujan/p/3320659.html)
8.2-sat
inline void add(int u, int a, int v, int b){ //加边 u = (u<<1) + a; v = (v<<1) + b; g[u].push_back(v^1); g[v].push_back(u^1); } bool dfs(int u){ if(mark[u]) return true; //如果已标记过 if(mark[u^1]) return false; //如果否命题假设为真,则矛盾 mark[u]=true; s[c++]=u; int i, tl=g[u].size(); for(i=0; i<tl; i++) if(!dfs(g[u][i])) return false; return true; } void solve(int n, int ave){ int i,j; memset(mark, 0, sizeof(mark)); for(i=0; i<n<<1; i+=2) if(!mark[i] && !mark[i+1]){ c=0; if(!dfs(i)){ //标记i while(c--) mark[s[c]]=false; if(!dfs(i+1)){ //标记否命题 printf("No solution.\n"); return ; } } } }
9.扩栈指令(用C++交才有效?):
#pragma comment(linker,"/STACK:102400000,102400000")
10.Bellman-Ford Algorithm 队列实现
queue<int> q; int bford(int s, int n){ int u,i,w,v; while(!q.empty()) q.pop(); q.push(s); // memset(cnt, 0, sizeof(cnt)); cnt[s]=1; memset(inq, 0, sizeof(inq)); inq[s]=1; for(i=0; i<n; i++) d[i]=INF; d[s]=0; while(!q.empty()){ u=q.front(); q.pop(); inq[u]=0; for(i=h[u]; i!=-1; i=e[i].nxt){ v=e[i].v; w=e[i].w; if(d[v]>d[u]+w){ d[v]=d[u]+w; if(!inq[v]){ // if(++cnt[v]>n) return 1; q.push(v); inq[v]=1; } } } } ans=d[s-1]-d[0]; // 结果不是d[s-1]么?。。。 return 0; }
11.朱刘算法(抄来:http://www.cnblogs.com/nanke/archive/2012/04/11/2441725.html#commentform)
int mdst(int s, int n, int m){ int u,v,i,d; int ret=0; while(true){ //找出每个点的最小入弧 for(i=0; i<n; i++) inv[i]=INF; for(i=0; i<m; i++){ u=e[i].u; v=e[i].v; d=e[i].d; if(d<inv[v] && u!=v){ inv[v]=d; pre[v]=u; if(u==s) roote=i; //要求的根 } } //当前点集合中有点不可达,则图不连通 for(i=0; i<n; i++) if(inv[i]==INF && i!=s) return -1; inv[s]=0; int cnt=0; memset(id, -1, sizeof(id)); memset(visit, -1, sizeof(visit)); for(i=0; i<n; i++){ //缩圈 ret+=inv[i]; v=i; //如果v还没标记且非虚根 while(visit[v]!=i && id[v]==-1 && v!=s){ visit[v]=i; v=pre[v]; } //这里表明上步visit[v]=i,即有圈 if(v!=s && id[v]==-1){ for(u=pre[v]; u!=v; u=pre[u]) id[u]=cnt; id[v]=cnt++; } } if(!cnt) break; //如果没圈 for(i=0; i<n; i++) if(id[i]==-1) id[i]=cnt++; for(i=0; i<m; i++){ //更新缩圈后各边 u=e[i].u; v=e[i].v; d=e[i].d; e[i]=(edge){id[u], id[v], (u==v ? d : d-inv[v])}; } //保证下次迭代选出正确的inv[v]值 n=cnt; s=id[s]; } return ret; }
12.欧拉回路
//判断欧拉回路: //1.图是否连通 2.是否存在奇点 3.打印欧拉回路 void dfs(int s){ for(int i=1; i<=50; i++) if(g[s][i]){ g[s][i]--; g[i][s]--; dfs(i); ans.push_back((edge){s,i}); } }
13.次小生成树(mst + dfs + 枚举边)
int p[MAXN]; //并查集 int finds(int x){ if(p[x]==-1) return x; else return (p[x]=finds(p[x])); } bool vis[MAXM]; int w[MAXN][MAXN]; int mst(){ //kruskal fill_n(p+1, n, -1); fill_n(vis, m, false); for(int i=1; i<=n; i++) g[i].clear(); sort(e, e+m); int ans=0; for(int i=0, j=0; i<m; i++){ int x = e[i].u, y = e[i].v, d = e[i].d; int fx = finds(x), fy = finds(y); if(fx!=fy){ p[fx]=fy; ans+=d; vis[i]=true; g[x].push_back(y); w[x][y]=w[y][x]=d; g[y].push_back(x); //请用双向边,否则可WAWAWA if(++j==n-1) break; } } return ans; } int maxcst[MAXN][MAXN]; vector<int> pre; //求mst中任意两点间的最大边权 void dfs(int u, int fa){ for(int i=0; i<pre.size(); i++){ int v = pre[i], d = w[fa][u]; maxcst[u][v]=maxcst[v][u]= MAX(d, maxcst[fa][v]); } pre.push_back(u); for(int i=0; i<g[u].size(); i++) if(fa!=g[u][i]) dfs(g[u][i], u); } //求mst,次小生成树权值 int fir = mst(); for(i=1; i<=n; i++) fill_n(maxcst[i]+1, n, -1); pre.clear(); dfs(1, -1); int sec = INF; for(i=0; i<m; i++) if(!vis[i]){ int u = e[i].u, v = e[i].v, d = e[i].d; sec=MIN(sec, fir-maxcst[u][v]+d); } printf("%d %d\n", fir, sec); //mst权值,次小生成树权值
14.优先队列优化的 Dijkstra
void Dijkstra(int s){ for(int i=0; i<=mVertex; i++) dis[i]=INF; dis[s]=0; priority_queue<node> q; q.push((node){0,s}); memset(done, 0, sizeof(done)); while(!q.empty()){ node t=q.top(); q.pop(); int u_ = t.u; if(done[u_]) continue; done[u_]=true; for(int x=h[u_]; x!=-1; x=next[x]) if(dis[v[x]]-dis[u_]>w[x]){ dis[v[x]] = dis[u_] + w[x]; p[v[x]] = x; q.push((node){dis[v[x]],v[x]}); } } }
15.2B了再补吧。。。