目录

  1. 图的类型与性质
    1.1 欧拉图
    1.2 哈密尔顿图

  2. 拓扑排序

  3. 最短路
    3.1 Dijkstra
        3.1.1 优先队列优化
        3.1.2 堆优化
        3.1.3 路径还原
    3.2 Bellman-Ford
        3.2.1 判断负环
    3.3 Floyd
    3.4 SPFA
    3.5 K短路
    3.6 差分约束系统

  4. 最小生成树
    4.1 Prmie
    4.2 Kruskal

  5. 二分图
    5.1 二分图判断
    5.2 二分图匹配(匈牙利算法)
    5.3 带权二分图匹配(KM算法)

  6. 最大团
    6.1 Bron-Kerbosch

  7. 连通图
    7.1 Tarjan

  8. 2-SAT

  9. 网络流
    9.1 最大流(Dicnic)
    9.2 最小费用流(spfa费用流)
    9.3 有界网络流
        9.3.1 无源汇上下界可行流
        9.3.2 有源汇上下界可行流
        9.3.3 有源汇上下界最大流
        9.3.4 有源汇上下界最小流
        9.3.5 有源汇上下界费用流


一.图的类型与性质

1.欧拉图

定义:

欧拉图是指通过图(无向图或有向图)中所有边且每边仅通过一次通路,相应的回路称为欧拉回路

性质:

1.无向连通图 G 是欧拉图,当且仅当 G 不含奇数度结点( G 的所有结点度数为偶数);
2.无向连通图G 含有欧拉通路,当且仅当 G 有零个或两个奇数度的结点;
3.有向连通图 D 是欧拉图,当且仅当该图为连通图且 D 中每个结点的入度=出度;
4.有向连通图 D 含有欧拉通路,当且仅当该图为连通图且 D 中除两个结点外,其余每个结点的入度=出度,且此两点满足 deg-(u)-deg+(v)=±1 。(起始点s的入度=出度-1,结束点t的出度=入度-1 或两个点的入度=出度);
5.一个非平凡连通图是欧拉图当且仅当它的每条边属于奇数个环;
6.如果图G是欧拉图且 H = G-uv,则 H 有奇数个 u,v-迹仅在最后访问 v ;同时,在这一序列的 u,v-迹中,不是路径的迹的条数是偶数。

判断欧拉图
void dfs(int now)
{int k;for(k=p[now];k!=-1;k=e[k].next){if(!vst[k]){vst[k]=true;vst[k^1]=true;dfs(e[k].to);ans[ansi++]=k;}}
}//dfs结束后,ans中存储的就是欧拉图,可通过vst判断图的联通性,每个点都被更新则全联通

2.哈密尔顿图

定义:

通过图G的每个结点一次,且仅一次的通路(回路),就是哈密顿通路(回路)。存在哈密顿回路的图就是哈密顿图。

性质:

(1)若图的最小度不小于顶点数的一半,则图是哈密顿图;
(2)若图中每一对不相邻的顶点的度数之和不小于顶点数,则图是哈密顿图。

//求汉密尔顿回路函数
int Hanmilton(){int path[1000] = {0};int cur_vertex = 0;     //作为保存当前结点int length = 0;         //汉密尔顿回路长度int min = 10000;        //最小长度for(int i = 1 ; i < this->Nv+1 ; i++){//对每个顶点为初始点进行比遍历寻找汉密尔顿回路length = 0;     //重新设置最端长度为0memset(this->isvisited,0,sizeof(this->isvisited[0])*(this->Nv+1));  //重新初始化访问数组为0this->isvisited[i] = 1;     //标记当前结点为已访问path[1] = i;        //保存到临时路径数组的第一个cur_vertex = i;     //保存当前顶点for(int j = 2 ; j < this->Nv+1 ; j++){//访问剩余的结点int k = 0;//寻找到第一个未访问的结点for(k = 2 ; k < this->Nv+1 ; k++){if(this->isvisited[k] == 0){break;}}int tmp = this->data[cur_vertex][k];        //保存当前顶点到该结点的路径长度for(int m = k+1 ; m < this->Nv+1 ; m++){//向后寻找有没有路径更短的节点if((!this->isvisited[m]) && (tmp > this->data[cur_vertex][m])){tmp = this->data[cur_vertex][m];//更新当前最短路径k = m;//更新第一个未被访问的结点}}path[j] = k;    //保存路径上的结点this->isvisited[k] = 1; //标记为已访问cur_vertex = k;     //跟新当前结点length += tmp;      //跟新长度if(length > min){   //当前长度大于最小长度,则改路径无效,跳出循环break;}}   length += this->data[cur_vertex][i];if(min > length){       //更新最小长度并保存最佳路径min = length;for(int m = 0 ; m < this->Nv+1 ; m++){this->best_path[m] = path[m];}}}//返回最小长度return min;
}//打印最佳汉密尔顿回路
void Print_Best_Path(){cout<<this->best_path[1];for(int i = 2 ; i < this->Nv+1 ; i++){cout<<" -> "<<this->best_path[i];}cout<<" -> "<<this->best_path[1];
}

二.拓扑排序

可求图的拓扑序、判断是否有环、判断是否有孤立点

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int MAX_N=10000;
struct edge
{int to,next;
}e[MAX_N];
int p[MAX_N],eid;
void init()
{eid=0;memset(p,-1,sizeof(p));
}
void insert(int u,int v)
{e[eid].to=v;e[eid].next=p[u];p[u]=eid++;
}
int indegree[MAX_N];
int n;
int topo()
{queue<int>Q;for(int i=1;i<=n;i++){if(indegree[i]==0)Q.push(i);}while(!Q.empty()){int now=Q.front();cout<<"visting"<<now<<endl;Q.pop();for(int i=p[now];i!=-1;i=e[i].next){int v=e[i].to;indegree[v]--;if(indegree[v]==0)Q.push(v);}}
}

三.最短路

1.Dijkstra

1.1 优先队列优化

不能处理负权边,O(E*logV)

#include <bits/stdc++.h>
#include <vector>
#include <queue>
using namespace std;const int MAX_N=1000;
const int MAX_V=1000;
const int INF=0x3f3f3f3f;
struct edge
{int to,cost;
};
typedef pair<int,int>P;    //first是最短距离,second是顶点编号
int V;
vector<edge>G[MAX_N];
int d[MAX_V];void Dijstra(int s)
{//通过指定greater<P>参数,堆按照first从小到大顺序取出值priority_queue< P,vector<P>,greater<P> > que;fill(d,d+V,INF);d[s]=0;que.push(P(0,s));while(!que.empty()){P now=que.top();que.pop();int v=now.second;if(d[v]<now.first) continue;for(int i=0;i<G[v].size();i++){edge e=G[v][i];if(d[e.to]>d[v]+e.cost){d[e.to]=d[v]+e.cost;que.push(P(d[e.to],e.to));}}}
}

1.2 堆优化:O((V+E)logV) 推荐使用!

const int MAX_N = 10000;
const int MAX_M = 100000;
const int inf = 0x3f3f3f3f;
struct edge {int v, w, next;
} e[MAX_M];
int p[MAX_N], eid;
int n;                        //顶点数设为全局变量
void mapinit() {memset(p, -1, sizeof(p));eid = 0;
}
void insert(int u, int v, int w) {  // 插入带权有向边e[eid].v = v;e[eid].w = w;e[eid].next = p[u];p[u] = eid++;
}
void insert2(int u, int v, int w) {  // 插入带权双向边insert(u, v, w);insert(v, u, w);
}
typedef pair<int, int> PII;
set<PII, less<PII> > min_heap;  // 用 set 来伪实现一个小根堆,并具有映射二叉堆的功能。堆中 pair<int, int> 的 second 表示顶点下标,first 表示该顶点的 dist 值
set<PII,less<PII> >:: iterator iter;
int dist[MAX_N];  // 存储单源最短路的结果
bool vst[MAX_N];  // 标记每个顶点是否在集合 U 中
bool dijkstra(int s) {// 初始化 dist、小根堆和集合 Umemset(vst, 0, sizeof(vst));memset(dist, 0x3f, sizeof(dist));min_heap.insert(make_pair(0, s));dist[s] = 0;for (int i = 0; i < n; ++i) {if (min_heap.size() == 0) {  // 如果小根堆中没有可用顶点,说明有顶点无法从源点到达,算法结束return false;}// 获取堆顶元素,并将堆顶元素从堆中删除iter = min_heap.begin();int v = iter->second;min_heap.erase(*iter);vst[v] = true;// 进行和普通 dijkstra 算法类似的松弛操作for (int j = p[v]; j != -1; j = e[j].next) {int x = e[j].v;if (!vst[x] && dist[v] + e[j].w < dist[x]) {// 先将对应的 pair 从堆中删除,再将更新后的 pair 插入堆min_heap.erase(make_pair(dist[x], x));dist[x] = dist[v] + e[j].w;min_heap.insert(make_pair(dist[x], x));}}}return true;  // 存储单源最短路的结果
}

1.3路径还原:基于Dijkstra,使用邻接矩阵

int prev[MAX_V];
int d[MAX_V];
int V;
int cost[MAX_V][MAX_V];
bool used[MAX_V]
void Dijkstra(int s)
{fill(d,d+v,INF);fill(used,used+V,false);fill(prev,prev+V,-1);d[s]=0;while(true){int v=-1;for(int u=0;u<V;u++)if(!used[u]&& (v==-1 || d[u]<d[v]) )v=u;if(v==-1) break;used[v]=true;for(int u=0;u<V;u++){if(d[u]>d[v]+cost[v][u])d[u]=d[v]+cost[v][u];prev[u]=v;}}
}
vector <int>get_path(int t)
{vector<int>path;for(;t!=-1;t=prev[t])path.push_back(t);reverse(path.begin(),path.end());return path;
}

2.Bellman-Ford

可处理负权边,O(V*E)

struct edge{int from,to,cost};
edge es[N];  //边
int d[N],V,S;    //最短距离,顶点数,边数void bellman_ford(int s)
{memset(d,INF,sizeof(d));d[s]=0;while(true){bool update = false;for(int i=0;i<E;i++){edge e = es[i];if(d[e.from] != INF && d[e.to]>d[e.from]+e.cost){d[e.to]=d[e.from]+e.cost;update = true;}}if(!update) break;}
}

2.1 检测负环

bool find_negative_loop()   //返回true表示存在负圈
{memset(d,0,sizeof(d));for(int i=0;i<V;i++){for(int j=0;j<E;j++){edge e=es[j];if(d[e.to]>d[e.from]+e.cost)d[e.to]=d[e.from]+e.cost;if(i==V-1)           //如果第n次仍然更新了,则存在负圈return truel}}return false;
}

3.Floyd

计算任意两点间最短路,O(v^3)

const int inf = 0x3f3f3f3f;
int g[MAX_N][MAX_N];  // 算法中的 G 矩阵
// 初始化 g 矩阵
void init() {for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {if (i == j) {g[i][j] = 0;} else {g[i][j] = inf;}}}
}
// 插入一条带权有向边
void insert(int u, int v, int w) {g[u][v] = w;
}
// 核心代码
void floyd() {for (int k = 0; k < n; ++k) {for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {if (g[i][k] + g[k][j] < g[i][j]) {g[i][j] = g[i][k] + g[k][j];}}}}
}

4.SPFA

可处理负权,但不能处理负环,能判断负环,稀疏图O(E),稠密图O(VE)

bool inq[MAX_N];
int cnt[MAX_N];  //记录每个顶点入队次数,若某点入队超过n次,则存在负环
int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
void spfa(int s)
{memset(inq, 0, sizeof(inq));memset(d, 0x3f, sizeof(d));d[s] = 0;inq[s] = true;queue<int> q;q.push(s);while (!q.empty()) {int u = q.front();q.pop();if(cnt[u]>n)cout<<存在负环;inq[u] = false;for (int i = p[u]; i != -1; i = e[i].next) {int v = e[i].v;if (d[u] + e[i].w < d[v]) {d[v] = d[u] + e[i].w;if (!inq[v]) {q.push(v);cnt[v]++;inq[v] = true;}}}}
}

5.K短路

SPFA+A*

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <queue>
using namespace std;int const maxn=10005;
int const maxm=1005;
int const INF=0x3f3f3f3f;int n,m,S,E,T,k;
int p1[maxn],p2[maxn],eid1,eid2;
bool vis[maxm];
int dis[maxm];
struct node
{int to,next,cost;
}e1[maxn],e2[maxn];struct node2
{int to,g,f;bool operator<(const node2 &r ) const{if(r.f==f)return r.g<g;return r.f<f;}
};
void init()
{eid1=1;eid2=1;memset(p1,-1,sizeof(p1));memset(p2,-1,sizeof(p2));
}void insert1(int u,int v,int w)       //正向存图
{e1[eid1].to=v;e1[eid1].cost=w;e1[eid1].next=p1[u];p1[u]=eid1++;
}
void insert2(int u,int v,int w)       //反向存图
{e2[eid2].to=v;e2[eid2].cost=w;e2[eid2].next=p2[u];p2[u]=eid2++;
}void spfa(int s)               //处理出从终点到所有点的最短路
{queue<int>Q;memset(vis,0,sizeof(vis));memset(dis,INF,sizeof(dis));dis[s]=0;vis[s]=1;Q.push(s);while(!Q.empty()){int u=Q.front();vis[u]=0;Q.pop();for(int i=p2[u];i!=-1;i=e2[i].next){int v=e2[i].to;if(dis[v]>dis[u]+e2[i].cost){dis[v]=dis[u]+e2[i].cost;if(!vis[v]){vis[v]=1;Q.push(v);}}}}
}int Astar()                 //A*求k短路
{node2 e,now;int cnt=0;priority_queue<node2>Q;if(S==E)k++;if(dis[S]==INF)return -1;e.to=S;e.g=0;e.f=e.g+dis[e.to];Q.push(e);while(!Q.empty()){e=Q.top();Q.pop();if(e.to==E)cnt++;if(cnt==k)return e.g;int u=e.to;for(int i=p1[u];i!=-1;i=e1[i].next){now.to=e1[i].to;now.g=e.g+e1[i].cost;now.f=now.g+dis[now.to];Q.push(now);if(now.g>T) return -1;}}return -1;
}
int main()
{//std::ios::sync_with_stdio(false);while(~scanf("%d%d",&n,&m)){init();scanf("%d%d%d%d",&S,&E,&k,&T);      //起点,终点,k短路,限定条件for(int i=1;i<=m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);insert1(u,v,w);insert2(v,u,w);}spfa(E);Astar();int ans=Astar();printf("%d",ans);   //输出k短路长度}
}

6.差分约束系统

即利用最短路算法解不等式,形如a-b<=k,则建立有向边a->b,权值为k
若a=b,即相当a<=b && a>=b ,即建立a->b 和 b->a 两条权值为0的边
加入超级源点,到达所有顶点,且权值为0
利用spfa算法,判断负环,若不存在,则该系统有解,若存在,则该系统无解


四.最小生成树

1.Prmie

O(n^2)

     int cost[MAX_N][MAX_N]; //cost[u][v] 表示边e=(u,v)的权值,不存在为INFint mincost[MAX_N];  //从集合T出发的边到每个顶点的最小权值bool used[MAX_N];    //顶点i是否包含在集合T中int n;           //顶点数int prim(){for(int i=0;i<n;++i)         //初始化{mincost[i]=INF;used[i]=false;}mincost[0]=0;int res=0;while(true){int v=-1;    //从不属于T的顶点中选取T到其权值最小的顶点for(int u=0;u<n;u++)if(!used[u] && (v==-1 || mincost[u]<mincost[v]))v=u;if(v==-1) break;  //没有可达点used[v]=true;   //把顶点v加入xres += mincost[v];  //更新结果for(int u=0;u<V;u++)mincost[u]=min(mincost[u],cost[v][u]);}return res;    //返回最小生成树总权值}

2.Kruskal

O(eloge)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100000;  // 最大顶点数
const int MAX_M = 100000;  // 最大边数
struct edge {int u, v, w;
}e[MAX_M];
int fa[MAX_N], n, m;  // fa 数组记录了并查集中结点的父亲
bool cmp(edge a,edge b) {return a.w < b.w;
}
// 并查集相关代码
int ancestor(int x) {  // 在并查集森林中找到 x 的祖先,也是所在连通块的标识if(fa[x] == x) return fa[x];else return fa[x] = ancestor(fa[x]);
}
int same(int x, int y) {  // 判断两个点是否在一个连通块(集合)内return ancestor(x) == ancestor(y);
}
void merge(int x, int y) {  // 合并两个连通块(集合)int fax = ancestor(x), fay = ancestor(y);fa[fax] = fay;
}
int Kruskal()
{for (int i = 1; i <= n; i++) {fa[i] = i;}int rst = n, ans = 0;  // rst 表示还剩多少个集合,ans 保存最小生成树上的总边权for (int i = 1; i <= m && rst > 1; i++) {int x = e[i].u, y = e[i].v;if (same(x, y)) {continue;  // same 函数是查询两个点是否在同一集合中} else {merge(x, y);  // merge 函数用来将两个点合并到同一集合中rst--;  // 每次将两个不同集合中的点合并,都将使 rst 值减 1ans += e[i].w;  // 这条边是最小生成树中的边,将答案加上边权}}return ans;
}
int main() {scanf("%d%d", &n, &m);  // n 为点数,m 为边数for (int i = 1; i <= m; i++) {scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);  // 用边集数组存放边,方便排序和调用}sort(e + 1, e + m + 1, cmp);  // 对边按边权进行升序排序cout<<Kruskal()<<endl;return 0;
}

五.二分图

定义

设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图

性质

(1)二分图的最大匹配数等于最小覆盖数,即求最少的点使得每条边都至少和其中的一个点相关联,很显然直接取最大匹配的一段节点即可。
(2)二分图的独立数等于顶点数减去最大匹配数,很显然的把最大匹配两端的点都从顶点集中去掉这个时候剩余的点是独立集,这是|V|-2*|M|,同时必然可以从每条匹配边的两端取一个点加入独立集并且保持其独立集性质。
(3)DAG的最小路径覆盖,将每个点拆点后作最大匹配,结果为n-m,求具体路径的时候顺着匹配边走就可以,匹配边i→j’,j→k’,k→l’….构成一条有向路径。
(4)最大匹配数=左边匹配点+右边未匹配点。因为在最大匹配集中的任意一条边,如果他的左边没标记,右边被标记了,那么我们就可找到一条新的增广路,所以每一条边都至少被一个点覆盖。
(5)最小边覆盖=图中点的个数-最大匹配数=最大独立集。

1.二分图判断

bool check()
{memset(used,-1,sizeof(used));queue<int>Q;Q.push(1);used[1]=0;while(!Q.empty()){int now=Q.front();for(int i=1;i<=n;i++)    //遍历所有点{if(map[now][i]==0)    //邻接矩阵存图continue;int v=i;if(used[v]==-1){used[v]=(used[now]+1)%2;Q.push(v);}else{if(used[v]==used[now])return false;}}Q.pop();}return true;
}

2.二分图匹配(匈牙利算法)

const int MAX_N = 100;  // X 集合中的顶点数上限
const int MAX_M = 10000;  // 总的边数上限
struct edge {int v, next;
} e[MAX_M];
int p[MAX_N], eid;
void init() {memset(p, -1, sizeof(p));eid = 0;
}
void insert(int u, int v) {  // 从 X 集合顶点 u 到 Y 集合顶点 v 连一条边,注意 u 和 v 的编号无关e[eid].v = v;e[eid].next = p[u];p[u] = eid++;
}
bool vst[MAX_N];  // 标记一次 dfs 过程中,Y 集合中的顶点是否已访问
int ans[MAX_N];  // 标记 Y 集合中的顶点匹配的 X 集合中的顶点编号
int n, m;  // n 表示 X 集合中的顶点数,假设顶点编号为 0..n-1
bool dfs(int u) {for (int i = p[u]; i != -1; i = e[i].next) {int v = e[i].v;if (!vst[v]) {  // 如果 Y 集合中的 v 还没有被访问vst[v] = true;if (ans[v] == -1 || dfs(ans[v])) {  // 如果 v 没有匹配点,或 v 的匹配点能找到一条到一个未匹配点的增广路,则将 v 的匹配点设为 uans[v] = u;return true;}}}return false;  // 没找到增广路
}
int maxmatch() {int cnt = 0;memset(ans, -1, sizeof(ans));  // 初始将所有 Y 集合中顶点的匹配编号设为 -1for (int i = 0; i < n; ++i) {memset(vst, 0, sizeof(vst));  // 进行 dfs 前,将 vst 清空cnt += dfs(i);  // 如果找到增广路,则将 cnt 累加 1}return cnt;  // cnt 是找到增广路的次数,也是总的最大匹配数
}

3.带权二分图匹配(KM算法)

#include <iostream>
#include <cstring>
#include <cstdio>using namespace std;
const int MAXN = 305;
const int INF = 0x3f3f3f3f;int love[MAXN][MAXN];   // 记录每个妹子和每个男生的好感度
int ex_girl[MAXN];      // 每个妹子的期望值
int ex_boy[MAXN];       // 每个男生的期望值
bool vis_girl[MAXN];    // 记录每一轮匹配匹配过的女生
bool vis_boy[MAXN];     // 记录每一轮匹配匹配过的男生
int match[MAXN];        // 记录每个男生匹配到的妹子 如果没有则为-1
int slack[MAXN];        // 记录每个汉子如果能被妹子倾心最少还需要多少期望值int N;bool dfs(int girl)
{vis_girl[girl] = true;for (int boy = 0; boy < N; ++boy) {if (vis_boy[boy]) continue; // 每一轮匹配 每个男生只尝试一次int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];if (gap == 0) {  // 如果符合要求vis_boy[boy] = true;if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一个没有匹配的男生 或者该男生的妹子可以找到其他人match[boy] = girl;return true;}} else {slack[boy] = min(slack[boy], gap);  // slack 可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值}}return false;
}int KM()
{memset(match, -1, sizeof match);    // 初始每个男生都没有匹配的女生memset(ex_boy, 0, sizeof ex_boy);   // 初始每个男生的期望值为0// 每个女生的初始期望值是与她相连的男生最大的好感度for (int i = 0; i < N; ++i) {ex_girl[i] = love[i][0];for (int j = 1; j < N; ++j) {ex_girl[i] = max(ex_girl[i], love[i][j]);}}// 尝试为每一个女生解决归宿问题for (int i = 0; i < N; ++i) {fill(slack, slack + N, INF);    // 因为要取最小值 初始化为无穷大while (1) {// 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止// 记录每轮匹配中男生女生是否被尝试匹配过memset(vis_girl, false, sizeof vis_girl);memset(vis_boy, false, sizeof vis_boy);if (dfs(i)) break;  // 找到归宿 退出// 如果不能找到 就降低期望值// 最小可降低的期望值int d = INF;for (int j = 0; j < N; ++j)if (!vis_boy[j]) d = min(d, slack[j]);for (int j = 0; j < N; ++j) {// 所有访问过的女生降低期望值if (vis_girl[j]) ex_girl[j] -= d;// 所有访问过的男生增加期望值if (vis_boy[j]) ex_boy[j] += d;// 没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!else slack[j] -= d;}}}// 匹配完成 求出所有配对的好感度的和int res = 0;for (int i = 0; i < N; ++i)res += love[ match[i] ][i];return res;
}
int main()
{while (~scanf("%d", &N)) {for (int i = 0; i < N; ++i)for (int j = 0; j < N; ++j)scanf("%d", &love[i][j]);printf("%d\n", KM());}return 0;
}        

六.最大团/极大团

定义

团:表示N 个点的集合,这N个点彼此两两连接,即有N(N-1)/2条边
极大团: 表示无法是其他团的子团。
最大团:点最多的极大团.

1.Bron–Kerbosch

#include<cstdio>
#include<cstring>
#define N 1010
/*
最大团 = 补图G的最大独立集数
———>最大独立集数 = 补图G'最大团
*/
//最大团模板
bool a[N][N];//a为图的邻接表(从1开始)
int ans, cnt[N], group[N], n, m, vis[N];//ans表示最大团,cnt[N]表示当前最大团的节点数,group[N]用以寻找一个最大团集合
bool dfs( int u, int pos )//u为当从前顶点开始深搜,pos为深搜深度(即当前深搜树所在第几层的位置)
{  int i, j;  for( i = u+1; i <= n; i++)//按递增顺序枚举顶点   {  if( cnt[i]+pos <= ans ) return 0;//剪枝   if( a[u][i] )   {  // 与目前团中元素比较,取 Non-N(i)   for( j = 0; j < pos; j++ ) if( !a[i][ vis[j] ] ) break;   if( j == pos )  {     // 若为空,则皆与 i 相邻,则此时将i加入到 最大团中   vis[pos] = i;//深搜层次也就是最大团的顶点数目,vis[pos] = i表示当前第pos小的最大团元素为i(因为是按增顺序枚举顶点 )   if( dfs( i, pos+1 ) ) return 1;      }      }  }      if( pos > ans )  {  for( i = 0; i < pos; i++ )  group[i] = vis[i]; // 更新最大团元素   ans = pos;  return 1;      }      return 0;
}
void maxclique()//求最大团
{  ans=-1;  for(int i=n;i>0;i--)  {  vis[0]=i;  dfs(i,1);  cnt[i]=ans;  }
}
int main()
{  //freopen("D:\in.txt","r",stdin);  int T;  //scanf("%d",&T);  while(~scanf("%d",&n))  {  if(n==0) break;  //scanf("%d%d",&n,&m );  int x, y;  memset( a, 0, sizeof(a));  /*for(int i = 0; i < m; i++){scanf("%d%d",&x,&y);a[x][y] = a[y][x] = 1;}*/  //相邻顶点间有边相连,模型转换成求 无向图 最大独立集。   //要求原图的最大独立集,转化为求原图的补图的最大团(最大团顶点数量 = 补图的最大独立集)      for(int i = 1; i <= n; i++)//求原图的补图   for(int j = 1; j <= n; j++)  scanf("%d",&a[i][j]);  maxclique();//求最大团   if( ans < 0 ) ans = 0;//ans表示最大团  printf("%d\n", ans );  /*for(int i = 0; i < ans; i++)printf( i == 0 ? "%d" : " %d", group[i]);//group[N]用以寻找一个最大团集合  if( ans > 0 ) puts("");*/  }
}  

七.连通图

定义

图中任意两点都是连通的,那么图被称作连通图

1.Tarjan

O(V+E)

#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=10000;
struct edge
{int to,next;
}e[MAX_N];
int p[MAX_N],eid;
void init()
{eid=0;memset(p,-1,sizeof(p));
}
void insert(int u,int v)
{e[eid].to=v;e[eid].next=p[u];p[u]=eid++;
}
int dfn[MAX_N],low[MAX_N];     //当前时间戳,最早次序号,dfn只能初始化为0!
int idx=0;                                   //时间戳初始化为0
int belong[MAX_N],scc=0;         //belong记录每个顶点属于哪个强连通分量,scc为强连通分量总数
int s[MAX_N],top=0;                 //模拟栈
bool instack[MAX_N];              //是否在栈中
void tarjan(int u)
{dfn[u]=low[u]=++idx;s[top++]=u;instack[u]=true;for(int i=p[u];i!=-1;i=e[i].next){int v=e[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(instack[v]){low[u]=min(low[u],dfn[v]);}}if(dfn[u]==low[u]){++scc;do{belong[s[--top]]=scc;instack[s[top]]=false;}while(s[top]!=u);}
}
int main()
{init();int n,m;cin>>n>>m;for(int i=0;i<m;i++){int a,b;cin>>a>>b;insert(a,b);}//   tarjan(0);for(int i=1;i<=n;i++)          //用这种方法更新全部点if(dfn[i]==0)tarjan(i);                    for(int i=1;i<=n;i++)cout<<i<<"->"<<belong[i]<<endl;return 0;
}

八.2-SAT

定义

有很多个集合,每个集合里面有若干元素,现给出一些取元素的规则,要判断是否可行,可行则给出一个可行方案。如果所有集合中,元素个数最多的集合有k个,那么我们就说这是一个k-sat问题,同理,2-sat问题就是k=2时的情况。

细节:

1.拆分后的点一定要用偶数作为编号起始,否则DFS中异或时会出错
2.DFS中只要有一个后继节点不成立,则返回falsefalse,这是因为后继节点们相当于可以被推导出来的条件,每一个都必须成立,原节点才有可能成立
3.DFS开头要判重
4.清空S数组时要注意下标
5.连接有向边时一定要注意,同时注意各个节点编号

struct TwoSAT
{int n;vector<int> G[N*2];bool mark[N*2];int S[N*2],c;int dfs(int x){if (mark[x^1]) return 0;if (mark[x]) return 1;    //和假设的值一样mark[x]=1;S[c++]=x;for (int i=0;i<G[x].size;i++)if (!dfs(G[x][i])) return 0;return 1;}//x=xval or y=yvalvoid add_clause(int x,int xv,int y,int yv){x=x*2+xv;y=y*2+yv;G[x^1].push_back(y);G[y^1].push_back(x);}void init(int n){this->n=n;for (int i=0;i<2*n;i++) G[i].clear();memset(mark,0,sizeof(mark));}bool solve(){for (int i=0;i<2*n;i+=2)   //枚举每一个点if (!mark[i]&&!mark[i+1])   //没有标记{  c=0;if (!dfs(i)){while (c>0) mark[S[--c]]=0;   //清空标记if (!dfs(i+1)) return 0;}}return 1;}
};

九.网络流

1.最大流(dicnic)

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;const int MAX_N=1000;
const int MAX_M=100000;
const int INF=0x3f3f3f3f;struct edge
{int v,c,next;
}e[MAX_M];int p[MAX_N],eid;
void init()
{memset(p,-1,sizeof(p));eid=0;
}
void insert(int u,int v,int c)
{e[eid].v=v;e[eid].next=p[u];e[eid].c=c;p[u]=eid++;
}
void addedge(int u,int v,int c)
{insert(u,v,c);insert(v,u,0);
}
int S,T;                 //源点和汇点
int d[MAX_N];       //d表示当前点的层数(level)
bool bfs()
{memset(d,-1,sizeof(d));queue<int>q;q.push(S);d[S]=0;while(!q.empty()){int u=q.front();q.pop();for(int i=p[u];i!=-1;i=e[i].next){int v=e[i].v;if(e[i].c>0 && d[v]==-1){q.push(v);d[v]=d[u]+1;}}}return (d[T]!=-1);
}int dfs(int u,int f)
{if(u==T)return f;int res=0;for(int i=p[u];i!=-1;i=e[i].next){int v=e[i].v;if(e[i].c>0 && d[u]+1 == d[v]){int tmp=dfs(v,min(f,e[i].c));      //递归计算顶点v,用c(u,v)更新当前流量上限f-=tmp;e[i].c-=tmp;res+=tmp;e[i^1].c+=tmp;                          //修改反向弧流量if(f==0)                                      //流量达到上限,不必继续搜索break;}}if(res==0)                    //当前没有经过顶点u的流,不必再搜索顶点ud[u]=-1;return res;
}int maxf()                 //计算最大流
{int res=0;while(bfs()){res+=dfs(S,INF);           //初始流量上限为INF}return res;
}int main()
{int t,cnt=1;cin>>t;while(t--){init();int m,n;cin>>n>>m;for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;addedge(a,b,c);}/*   for(int k=1;k<=n;k++)                       //输出图,用于检测图的读入是否正确for(int i=p[k];i!=-1;i=e[i].next){cout<<k<<"->"<<e[i].v<<" "<<e[i].c<<endl;}*/S=1;T=n;cout<<"Case "<< cnt++ <<": ";cout<<maxf()<<endl;}return 0;
}

2.最小费用流(spfa费用流)

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;const int MAX_N = 10005;
const int MAX_M = 100005;
const int inf = 0x3f3f3f3f;struct edge
{int v, c, w, next;  // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
} e[MAX_M];int p[MAX_N], s, t, eid;  // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕void init()
{memset(p, -1, sizeof(p));eid = 0;
}void insert(int u, int v, int c, int w) {e[eid].v = v;e[eid].c = c;e[eid].w = w;e[eid].next = p[u];p[u] = eid++;
}
void addedge(int u, int v, int c, int w) {insert(u, v, c, w);insert(v, u, 0, -w);
}bool inq[MAX_N];
int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
int pre[MAX_N];  // 最短路中连向当前顶点的边的编号bool spfa()
{  // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 truememset(inq, 0, sizeof(inq));memset(d, 0x3f, sizeof(d));memset(pre, -1, sizeof(pre));d[s] = 0;inq[s] = true;queue<int> q;q.push(s);while (!q.empty()) {int u = q.front();q.pop();inq[u] = false;for (int i = p[u]; i != -1; i = e[i].next) {if (e[i].c) {int v = e[i].v;if (d[u] + e[i].w < d[v]) {d[v] = d[u] + e[i].w;pre[v] = i;if (!inq[v]) {q.push(v);inq[v] = true;}}}}}return pre[t] != -1;
}int costflow() {  // 计算最小费用最大流int ret = 0;  // 累加和while(spfa()) {int flow = inf;for(int i = t; i != s; i = e[pre[i]^1].v) {flow = min(e[pre[i]].c, flow);  // 计算当前增广路上的最小流量}for(int i = t; i != s; i = e[pre[i]^1].v) {e[pre[i]].c -= flow;e[pre[i]^1].c += flow;ret += e[pre[i]].w * flow;}}return ret;
}int main()         //以最短来回路为例
{int n,m;cin>>n>>m;init();s=0;t=n+1;addedge(s,1,2,0);for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;addedge(a,b,1,c);addedge(b,a,1,c);}addedge(n,t,2,0);int ans=costflow();cout<<ans<<endl;return 0;
}

3.有界网络流

https://www.cnblogs.com/mlystdcall/p/6734852.html

3.1无源汇上下界可行流

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=300,maxm=100000;
struct edge{int to,next,w,num;
}lst[maxm];int len=0,first[maxn],_first[maxn];
void addedge(int a,int b,int w,int num){lst[len].num=num;lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;lst[len].num=num;lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
int vis[maxn],dis[maxn],q[maxn],head,tail,s,t,T;
bool bfs(){vis[s]=++T;dis[s]=1;head=tail=0;q[tail++]=s;while(head!=tail){int x=q[head++];for(int pt=first[x];pt!=-1;pt=lst[pt].next){if(lst[pt].w&&vis[lst[pt].to]!=T){vis[lst[pt].to]=T;dis[lst[pt].to]=dis[x]+1;q[tail++]=lst[pt].to;}}}if(vis[t]==T)memcpy(_first,first,sizeof(first));return vis[t]==T;
}
int dfs(int x,int lim){if(x==t){return lim;}int flow=0,a;for(int pt=_first[x];pt!=-1;pt=lst[pt].next){_first[x]=pt;if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;if(flow==lim)return flow;}}return flow;
}
int dinic(){int ans=0,x;while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;return ans;
}
int low[maxm],ans[maxm];
int totflow[maxn],n,m;void work(){memset(totflow,0,sizeof(totflow));memset(first,-1,sizeof(first));len=0;scanf("%d%d",&n,&m);int u,v,b;s=0;t=n+1;for(int i=1;i<=m;++i){scanf("%d%d%d%d",&u,&v,&low[i],&b);addedge(u,v,b-low[i],i);totflow[u]-=low[i];totflow[v]+=low[i];}int sum=0;for(int i=1;i<=n;++i){if(totflow[i]<0){addedge(i,t,-totflow[i],0);}else{sum+=totflow[i];addedge(s,i,totflow[i],0);}}if(dinic()==sum){puts("YES");for(int i=1;i<=n;++i){for(int pt=first[i];pt!=-1;pt=lst[pt].next){if(lst[pt].num==0||pt%2==0)continue;ans[lst[pt].num]=lst[pt].w+low[lst[pt].num];}}for(int i=1;i<=m;++i)printf("%d\n",ans[i]);}else puts("NO");
}
int main(){int tests;scanf("%d",&tests);while(tests--){work();if(tests)printf("\n");}return 0;
}

3.2有源汇上下界可行流

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<iostream>
using namespace std;
const int N=1e5+7;
const int inf=0x3f3f3f3f;struct node
{int to,next,cost;
}e[30*N];
int eid,p[N],c[N];void init()
{eid=0;memset(p,-1,sizeof(p));memset(c,-1,sizeof(c));
}
void insert(int u,int v,int w)
{e[eid].to=v;e[eid].cost=w;e[eid].next=p[u];p[u]=eid++;
}
void addedge(int u,int v,int w)
{insert(u,v,w);insert(v,u,0);
}int n,m,sp,tp;
int d[N];
bool bfs()                      //构建层次
{memset(d,-1,sizeof(d));queue<int>Q;d[sp]=0;Q.push(sp);while(!Q.empty()){int u=Q.front();Q.pop();for(int i=p[u];i!=-1;i=e[i].next){int v=e[i].to;if(d[v]==-1&&e[i].cost){d[v]=d[u]+1;Q.push(v);if(v==tp) return true;}}}return ~d[tp];
}
int dfs(int u,int b)
{if(u==tp) return b;int r=0;for(int i=c[u];i!=-1;i=e[i].next){int v=e[i].to;if(e[i].cost&&d[v]==d[u]+1){int x=min(e[i].cost,b-r);c[u]=i;x=dfs(v,x);r+=x;e[i].cost-=x;e[i^1].cost+=x;if(r==b) break;}}if(!r)d[u]=-2;return r;
}
int dinic(){int total=0,tmp;while(bfs()){memcpy(c,p,sizeof(p));while(tmp=dfs(sp,inf))total+=tmp;}return total;
}
int x[N];
int main()
{int k,L,R,cs=0;while(scanf("%d%d%d",&n,&m,&k)!=EOF){init();sp=n+m+1,tp=n+m+2;          //附加源,附加汇int ss=n+m+3,tt=n+m+4;      //源点,汇点scanf("%d%d",&L,&R);int u,v;for(int i=1;i<=k;i++){scanf("%d%d",&u,&v);addedge(u,v+n,1);       //构建网络,左边点编号为1~n,右边点编号为n+1~2*n}for(int i=1;i<=n;i++){addedge(ss,i,R-L);     //源点连向所有左侧点,修改流量为R-Laddedge(sp,i,L);       //附加源连向所有左侧点,流量为L,表示必要弧addedge(ss,tp,L);      //源点连向附加汇}for(int i=1;i<=m;i++){addedge(i+n,tt,R-L);    //右侧点连向汇点,修改流量为R-Laddedge(sp,tt,L);       //附加源连向汇点addedge(i+n,tp,L);      //右侧点连向附加汇,流量为L,表示必要弧}addedge(tt,ss,inf);      //汇点到源点连一条流量为inf的边,变为有源上下界网络流printf("Case %d: ",++cs);if(dinic()==(n+m)*L) puts("Yes");     //求附加源到附加汇的最大流,若满足附加源到附加汇的所有弧都满流,则有可行流else puts("No");}return 0;
}

3.3有源汇上下界最大流

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2005,maxm=100005;
const int inf=0x7f7f7f7f;
struct edge{int to,next,w,num;
}lst[maxm];int len=0,first[maxn],_first[maxn];
void addedge(int a,int b,int w,int num){lst[len].num=num;lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;lst[len].num=num;lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
int q[maxn],vis[maxn],dis[maxn],T,s,t,head,tail,ss,tt;
bool bfs(){head=tail=0;vis[s]=++T;q[tail++]=s;while(head!=tail){int x=q[head++];for(int pt=first[x];pt!=-1;pt=lst[pt].next){if(lst[pt].w&&vis[lst[pt].to]!=T){vis[lst[pt].to]=T;dis[lst[pt].to]=dis[x]+1;q[tail++]=lst[pt].to;}}}if(vis[t]==T)memcpy(_first,first,sizeof(first));return vis[t]==T;
}
int dfs(int x,int lim){if(x==t)return lim;int flow=0,a;for(int pt=_first[x];pt!=-1;pt=lst[pt].next){_first[x]=pt;if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;if(flow==lim)return flow;}}return flow;
}
int dinic(){int ans=0,x;while(bfs())while(x=dfs(s,inf))ans+=x;return ans;
}
int totflow[maxn];
void Add(int a,int b,int lo,int hi,int num){totflow[a]-=lo;totflow[b]+=lo;addedge(a,b,hi-lo,num);
}
int low[maxm],ans[maxm];
int n,m,tot;
void bound_flow(){int sum=0;for(int i=s;i<=t;++i){if(totflow[i]<0){addedge(i,tt,-totflow[i],0);}else{sum+=totflow[i];addedge(ss,i,totflow[i],0);}}addedge(t,s,0x7f7f7f7f,0);int tmps=s,tmpt=t;s=ss;t=tt;if(dinic()==sum){for(int pt=first[ss];pt!=-1;pt=lst[pt].next){lst[pt].w=lst[pt^1].w=0;}for(int pt=first[tt];pt!=-1;pt=lst[pt].next){lst[pt].w=lst[pt^1].w=0;}int flow0=lst[len-1].w;lst[len-1].w=lst[len-2].w=0;s=tmps;t=tmpt;printf("%d\n",flow0+dinic());for(int i=1;i<=m;++i){for(int pt=first[i+n];pt!=-1;pt=lst[pt].next){if(lst[pt].num!=0){ans[lst[pt].num]=lst[pt].w+low[lst[pt].num];}}}for(int i=1;i<=tot;++i)printf("%d\n",ans[i]);}else{printf("-1\n");}
}void work(){s=0;t=n+m+1;ss=n+m+2;tt=n+m+3;memset(first,-1,sizeof(first));len=0;memset(totflow,0,sizeof(totflow));int x,y;for(int i=1;i<=m;++i){scanf("%d",&x);Add(n+i,t,x,inf,0);}int l,h;tot=0;for(int i=1;i<=n;++i){scanf("%d%d",&x,&y);Add(s,i,0,y,0);for(int j=1;j<=x;++j){++tot;scanf("%d%d%d",&y,&l,&h);Add(i,n+y+1,l,h,tot);low[tot]=l;}}bound_flow();printf("\n");
}
int main(){while(scanf("%d%d",&n,&m)!=EOF)work();return 0;
}

3.4有源汇上下界最小流

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=205,maxm=100005;
struct edge{int to,next,w;
}lst[maxm];int len=0,first[maxn],_first[maxn];
void addedge(int a,int b,int w){//printf("Add %d %d\n",a,b);lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
int q[maxn],vis[maxn],dis[maxn],head,tail,s,t,T,ss,tt;
bool bfs(){head=tail=0;vis[s]=++T;dis[s]=1;q[tail++]=s;while(head!=tail){int x=q[head++];for(int pt=first[x];pt!=-1;pt=lst[pt].next){if(lst[pt].w&&vis[lst[pt].to]!=T){vis[lst[pt].to]=T;dis[lst[pt].to]=dis[x]+1;q[tail++]=lst[pt].to;}}}if(vis[t]==T)memcpy(_first,first,sizeof(first));return vis[t]==T;
}
int dfs(int x,int lim){if(x==t)return lim;int flow=0,a;for(int pt=_first[x];pt!=-1;pt=lst[pt].next){_first[x]=pt;if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;if(flow==lim)return flow;}}return flow;
}
int dinic(){int ans=0,x;while(bfs()){while(x=dfs(s,0x7f7f7f7f))ans+=x;}return ans;
}
int totflow[maxn];
void del(int x){for(int pt=first[x];pt!=-1;pt=lst[pt].next)lst[pt].w=lst[pt^1].w=0;
}
int main(){int n;scanf("%d",&n);int x,y;memset(first,-1,sizeof(first));for(int i=1;i<=n;++i){scanf("%d",&x);for(int j=1;j<=x;++j){scanf("%d",&y);totflow[i]--;totflow[y]++;addedge(i,y,0x7f7f7f7f);}}s=0;t=n+1;ss=n+2,tt=n+3;for(int i=1;i<=n;++i){addedge(s,i,0x7f7f7f7f);addedge(i,t,0x7f7f7f7f);}for(int i=1;i<=n;++i){if(totflow[i]<0){addedge(i,tt,-totflow[i]);}else{addedge(ss,i,totflow[i]);}}addedge(t,s,0x7f7f7f7f);int tmps=s,tmpt=t;s=ss;t=tt;dinic();int flow0=lst[len-1].w;lst[len-1].w=lst[len-2].w=0;del(ss);del(tt);s=tmpt;t=tmps;printf("%d\n",flow0-dinic());return 0;
}

3.5有源汇上下界费用流

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#define N 50000
#define inf 1000000000
using namespace std;
int ans,n,m,tot,val[N];
int point[N],v[N],next[N],remain[N],c[N],dis[N],can[N],last[N];
void add(int x,int y,int z,int k)
{tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; c[tot]=k;tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; c[tot]=-k;//if (z)  cout<<x<<" "<<y<<" "<<z<<" "<<k<<endl;
}
int addflow(int s,int t)
{int now=t; int ans=inf;while (now!=s) {//cout<<now<<" ";ans=min(ans,remain[last[now]]);now=v[last[now]^1];}//cout<<now<<endl;now=t;while (now!=s) {remain[last[now]]-=ans;remain[last[now]^1]+=ans;now=v[last[now]^1];}return ans;
}
bool spfa(int s,int t)
{for (int i=0;i<=t;i++) dis[i]=inf,can[i]=0;dis[s]=0; can[s]=1;queue<int> p; p.push(s);while (!p.empty()){int now=p.front(); p.pop();for (int i=point[now];i!=-1;i=next[i])if (remain[i]&&dis[v[i]]>dis[now]+c[i]){dis[v[i]]=dis[now]+c[i];last[v[i]]=i;if (!can[v[i]]) {can[v[i]]=1;p.push(v[i]);}}can[now]=0;}if (dis[t]==inf) return false;int mx=addflow(s,t);//cout<<dis[t]<<" "<<mx<<endl;ans+=mx*dis[t];return true;
}
void solve(int s,int t)
{while (spfa(s,t));
}
int main()
{freopen("a.in","r",stdin);freopen("my.out","w",stdout);while (scanf("%d%d",&n,&m)!=EOF) {tot=-1;memset(point,-1,sizeof(point));int t=2*(n+1)+1;for (int i=1;i<=n;i++) {int x; scanf("%d",&x);add(1,i+1,1,x); }for (int i=1;i<=n;i++)for (int j=i+1;j<=n;j++){int x; scanf("%d",&x);add(i+n+1,j+1,1,x);}for (int i=1;i<=n;i++) add(i+1,i+n+1,0,0),add(i+n+1,t,1,0);int S=t+1; int T=t+2;add(t,0,inf,0); add(0,1,m,0); //add(S,1,1,0); add(0,T,1,0);for (int i=2;i<=n+1;i++)add(S,i+n,1,0),add(i,T,1,0);solve(S,T);printf("%d\n",ans);}
}

转载于:https://www.cnblogs.com/floatingcloak/p/10344094.html

ACM-图论完全总结(知识点+模板)相关推荐

  1. ACM图论之存图方式

    <转--剑紫青天> 对于ACM图论方面的题目总是免不了首先要建图存图,使用合适的存图方式不但是AC的必要条件,解题事半功倍. 以下主要分析三种常见的存图方式的优缺点以及代码实现 邻接矩阵 ...

  2. 【阿良的算法之路】图论最短路算法模板

    图论: [阿良的算法之路]图论最短路算法模板 [模板]dirjkstra单源最短路径 [模板]Bellman-Ford多源最短路 [模板]Spfa求最短路 [模板]Spfa判断负环 [模板]Floya ...

  3. 数学--图论--莫比乌斯线性筛模板

    ACM常用模板合集 int prime[MAXN],prime_tot; bool isprime[MAXN]; int mu[MAXN]; void pre_calc(int limt) {mu[1 ...

  4. 图论--最短路--SPFA模板(能过题,真没错的模板)

    [ACM常用模板合集] #include<iostream> #include<queue> #include<algorithm> #include<set ...

  5. 连通域最小外接矩形算法原理_算法|图论 2W字知识点整理(超全面)

    作者:SovietPower✨ 链接:https://ac.nowcoder.com/discuss/186584 来源:牛客网 度数序列 对于无向图, 为每个点的度数.有 (每条边被计算两次).有偶 ...

  6. 【模板】ACM Conference的Latex论文模板与说明

    转载请注明出处:小锋学长生活大爆炸[xfxuezhang.blog.csdn.net] 目录 cls文件说明 ACM会议论文模板 9pt和10pt模板 cls文件说明 原版文件链接:https://c ...

  7. ACM图论+数据结构杂题总结

    ACM:图论+数据结构杂题总结 T1: 题目描述:(出处:Atcoder Regular Contest 067 Yakiniku Restaurants) 一条街上有N家烧烤餐馆.餐厅从西到东编号为 ...

  8. 图论-有向图的连通性模板题(hdu1296)(hdu1827)

    1.强连通分量: 强连通分量可以理解为边数最少的情况下是一个环. 这里写了一个模板题用的是tarjan算法,当然还有其他算法. tarjan算法的关键其实还是对于num数组和low数组的使用 然后可以 ...

  9. ~~朴素dijkstra算法 (搜索与图论)(附模板题AcWing 849. Dijkstra求最短路 I)

    模板 时间复杂是 O(n2+m), n表示点数,m 表示边数 int g[N][N]; // 存储每条边 int dist[N]; // 存储1号点到每个点的最短距离 bool st[N]; // 存 ...

  10. java ACM竞赛IO优化Petr模板

    相信ACMer 用java的时候不少遇到超时的现象吧,今天做CF的时候就遇到了,$10^5$ Int 就超1s了,根本不能忍,这里留下一个petr 大佬的模板,果然速度提高10倍 输入 输出 例子 输 ...

最新文章

  1. 【ACM】杭电OJ 1096
  2. aspx文件、aspx.cs文件、aspx.designer.cs文件之讲解
  3. 跨域理解及服务器端解决跨域问题
  4. JQuery实现树的功能doc
  5. 共聚焦图片怎么加标尺_科研教程|利用PS给电镜加标尺
  6. HttpClient官方sample代码的深入分析(连接池)
  7. 八皇后(N皇后)问题
  8. 2019 CCF CSP-J2题解
  9. 在线客服代码,可以用
  10. 03. Django基础:URL和视图函数
  11. 本地文件搜索神器everything介绍
  12. openwrt 进入failsafe模式
  13. 浅谈智能搜索和对话式OS
  14. springboot 整合阿里云oss
  15. 什么命令能查看服务器的型号,查看服务器型号的命令
  16. word如何去除表格中高亮部分(表格属性-边框和底纹)
  17. pytorch创建自己的数据集(分类任务)
  18. Oracle数据库设计方法
  19. GAN之父Ian Goodfellow离职苹果:不想重返办公室工作
  20. mybatis-plus代码生成器,一键生成代码

热门文章

  1. 英文商务电邮实用技巧
  2. EasyRules动态规则实现
  3. “蚁景杯”WUSTCTF2021新生赛writeup
  4. IIR数字滤波器原理与应用
  5. Vue tsx 使用自定义v-model修饰符
  6. Uncaught (in promise) TypeError: Cannot read property ‘coupon‘ of undefined,页面渲染出来了,但是报上面的错误
  7. Linux下lsof命令详解
  8. js实现单选框的选择
  9. unity3d 摄像机跟随角色时 画面抽搐问题
  10. C++ Reference: Standard C++ Library reference: C Library: cstdio: vsnprintf