Sort
void Sort(int a[], int L, int R) {
if (L >= R) return ;
int x = a[L], i = L, j = R;
while (i != j) {
while (i != j && a[j] >= x) j--;
a[i] = a[j];
while (i != j && a[i] <= x) i++;
a[j] = a[i];
}
a[i] = x;
Sort(a, L, i-1);
Sort(a, i+1, R);
}
最短路径
INPUT:
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示节点数,标号为1的节点是起点,标号为N的节点是终点,M则表示有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在节点A与节点B之间有一条路,长度为C输入保证至少存在1条可行路线
OUTPUT:
对于每组输入,输出一行,表示从起点到终点的最短路程
Floyd
void Floyed() {
for (int k=1; k<=n; k++) //中介点要放在最外层
for (int i=1; i<=n; i++) if (i != k)
for (int j=1; j<=n; j++) if (j != k && j != i)
if (mp[i][j] > mp[i][k] + mp[k][j])
mp[i][j] = mp[i][k] + mp[k][j];
}
Dijkstra+邻接矩阵
#include<bits/stdc++.h>
using namespace std;
const int N = 107;
const int M = 10007;
int mp[N][N], n, m;
int dis[N]; //dis[i]表示起点与点i的距离
bool mark[N];
void Dijkstra(int st, int ed) {
memset(dis, 127/3, sizeof(dis));
memset(mark, false, sizeof(mark));
dis[st] = 0;
while (1) {
int p = 0;
for (int i=1; i<=n; i++)
//在所有未确定的点中找与起点路径最短的点作为确定点(贪心法)
if (!mark[i] && dis[i] < dis[p]) p = i;
if (!p || p == ed) break;
//若起点与所有与起点联通的点的最短路都已找到(p==0),或终点被找到(p==ed),就退出循环
mark[p] = true; //将p点设置为已确定
for (int i=1; i<=n; i++) //用p点更新与p点相连的点的最短路
if (!mark[i] && dis[i] > dis[p] + mp[p][i])
//加上 !mark[i] 可稍微提高一点效率
dis[i] = dis[p] + mp[p][i];
}
}
int main()
{
while (scanf("%d %d", &n, &m) && n && m) {
memset(mp, 127/3, sizeof(mp));
int u, v, w, st = 1, ed = n;
while (m--) {
scanf("%d %d %d", &u, &v, &w);
mp[u][v] = mp[v][u] = w;
}
Dijkstra(st, ed);
printf("%d\n", dis[ed]);
}
return 0;
}
Dijkstra+邻接表+队列优化
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1007;
const int M = 10007;
struct Edge {
int to, val, next;
}e[M << 1];
int head[N], dis[N], n, m, cnt;
bool mark[N];
struct cmp{
bool operator ()(const int &a, const int &b) {
return dis[a] > dis[b]; //定义">",建立小顶堆
}
};
priority_queue <int, vector<int>, cmp> q; //定义优先队列,自定义排序规则cmp
void addedge(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].val = w;
e[cnt].next = head[u];
head[u] = cnt;
}
void Dijkstra(int st, int ed) {
memset(dis, 127/3, sizeof(dis));
memset(mark, false, sizeof(mark));
while (!q.empty()) q.pop(); //清空队列
dis[st] = 0;
q.push(1);
while (1) {
//因为同一个点可能多次入队,所以队列中的点可能已是确定点
//如遇到确定点,就出队丢弃
while (!q.empty() && mark[q.top()]) q.pop();
if (q.empty()) break; //队列为空表示与起点联通的点的最短路均已确定
int p = q.top();
q.pop();
if (p == ed) break; //与终点的最短路已找到
mark[p] = true;
for (int i=head[p]; i; i=e[i].next)
if (!mark[e[i].to] && dis[e[i].to] > dis[p] + e[i].val) {
dis[e[i].to] = dis[p] + e[i].val;
q.push(e[i].to); //将距离有变化的点入队
}
}
}
int main() {
freopen("data.in", "r", stdin);
while (scanf("%d %d", &n, &m) && n && m) {
memset(e, 0, sizeof(e));
memset(head, 0, sizeof(head));
cnt = 0;
int u, v, w, st = 1, ed = n;
while (m--) {
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
}
Dijkstra(st, ed);
printf("%d\n", dis[ed]);
}
return 0;
}
SPFA
O(kE) E是边数,k是常数,平均值为2
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 107;
const int M = 10007;
struct Edge {
int to, val, next;
}e[M << 1];
int head[N], dis[N], pre[N], n, m, cnt;
int times[N]; //统计入队次数,若某点入队次数达到n次,表示该图有负权环
bool exist[N]; //设置exist[],是为了避免重复入队
void addedge(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].val = w;
e[cnt].next = head[u];
head[u] = cnt;
}
queue <int> q;
int SPFA(int st) { //返回值为True表示不存在负权环,反之存在
memset(dis, 127/3, sizeof(dis));
memset(exist, false, sizeof(exist));
memset(times, 0, sizeof(times));
dis[st] = 0; //设置起点
q.push(st); //起点入队
exist[st] = true; //标志起点已入队
times[st]++;
while (!q.empty()) {
int p = q.front(); //从队中取出一个节点
q.pop();
exist[p] = false;
for (int i=head[p]; i; i=e[i].next) //遍历与此节点相邻的边,对与之相邻的点更新
if (dis[e[i].to] > dis[p] + e[i].val) {
dis[e[i].to] = dis[p] + e[i].val;
pre[e[i].to] = p; //记录路径:e[i].to的前一个节点是p
if (!exist[e[i].to]) { //如有节点被更新,则将该点入队,以便更新其他点
q.push(e[i].to);
exist[e[i].to] = true;
times[e[i].to]++;
if (times[e[i].to] >= n) return false; //检查是否存在负权环
}
}
}
return true;
}
void print(int p) {
if (pre[p]) print(pre[p]);
else {printf("%d", p); return; }
printf("->%d", p);
}
int main()
{
freopen("data.in", "r", stdin);
while (scanf("%d %d", &n, &m) && n && m) {
memset(e, 0, sizeof(e));
memset(head, 0, sizeof(head));
cnt = 0;
int u, v, w, st = 1, ed = n;
while (m--) {
scanf("%d %d %d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
}
if (SPFA(st)) {
printf("%d\n", dis[ed]);
// print(ed); //输出路径
// printf("\n");
}
}
return 0;
}
最小生成树
Kruskal
n(n<=100)个节点 f(i,j)表示i,j之间路径长度(f(i,j)<=1000) ,f(i,j)为0表示i,j之间无路径 ,要使每一个节点均连通.求Σf(i,j)min
INPUT:第一行两个正整数n k,接下来的k行每行三个正整数i j m
OUTPUT:一行,Σf(i,j)min
#include<bits/stdc++.h>
using namespace std;
struct Edge{
int u,v,w;
}e[11000];
int cnt=0,n,tot=0,ans=0,sum=0;
int a,i,j,k,x,y,w;
int fa[1000]={0};
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
int find(int x){
if(fa[x]==x)return fa[x];
else return fa[x]=find(fa[x]);
}
void Together(int x,int y){
int fx=find(x);
int fy=find(y);
fa[fx]=fy;
}
int main(){
scanf("%d%d",&n,&k);
for(i=1;i<=k;i++)fa[i]=i;
for(i=1;i<=k;i++){
scanf("%d %d %d",&x,&y,&w);
e[i].u=x; e[i].v=y; e[i].w=w;
}
sort(e+1,e+k+1,cmp);
int T=0;
for(i=1;i<=k;i++){
x=e[i].u;y=e[i].v;
int fx,fy;
fx=find(x);fy=find(y);
if(fx!=fy){
Together(x,y);
tot+=e[i].w;
T++;
}
if(T==n-1)break;
}
printf("%d",tot);
return 0;
}
LCA(最近公共祖先)
#include <bits/stdc++.h>
using namespace std;
const int N = 50007;
const int M = 75007;
struct Edge{
int to, w, next;
}e[N<<1];
int n, head[N], cnt, fa[N][20], dis[N], deep[N];
void addedge(int u, int v, int w){
e[++cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt;
}
void dfs(int cur, int father, int w){ //dfs函数:先处理cur的所有信息,再往下递归
deep[cur] = deep[father] + 1 ; //cur的深度(根节点的深度设为1)
dis[cur] = dis[father] + w; //cur到根节点的距离
fa[cur][0] = father; //cur的父节点(没有父亲就等于0,根节点的父亲就是0)
for (int i=1; i<16; i++) { //由于cur的祖先的fa[]都已经计算完毕,所以可以用来计算cur的各级祖先
if (!fa[cur][i-1]) break; //如果cur没有2^(i-1)级祖先,自然不会有更高级的祖先,直接break
fa[cur][i] = fa[fa[cur][i-1]][i-1];
}
for (int i=head[cur]; i; i=e[i].next)
if (e[i].to != father) dfs(e[i].to, cur, e[i].w);
//同时存在u->v与v->u二条边
}
int LCA(int u, int v){
if (deep[u] < deep[v]) swap(u, v); //深度大的为u
int k = deep[u] - deep[v]; //k等于u与v的深度差
for (int i=0; i<=16; i++) //此循环用于提到深度相同。
if (1<<i & k) u = fa[u][i];
if (u == v) return v; //u等于v说明v是u的祖先,也就是它们的LCA
for (int i=16; i>=0; i--) //u与v一起上升,此处循环必须是从大到小!
if (fa[u][i] != fa[v][i]){ //因为我们应该越提越“精确”,这是关键
u = fa[u][i];
v = fa[v][i];
}
return fa[u][0]; //此时u、v的第一个父亲就是LCA。
}
int main()
{
//freopen("data.in", "r", stdin);
int m, u, v, w;
scanf("%d", &n);
for (int i=1; i<n; i++){
scanf("%d %d %d", &u, &v, &w);
u++; v++;
addedge(u, v, w);
addedge(v, u, w);
}
dfs(1, 0, 0);
scanf("%d", &m);
while (m--) {
scanf("%d %d", &u, &v);
u++; v++;
printf("%d\n", dis[u] + dis[v] - 2*dis[LCA(u, v)]);
}
return 0;
}
二分图
二分图最大匹配(匈牙利算法)
#include <bits/stdc++.h>
using namespace std;
const int N = 507;
bool mp[N][N], vis[N];
//将二分图分成左图和右图,与右点匹配的左点记录在match[]中
int match[N], n, m;
bool dfs(int u){
for (int v=1; v<=m; v++) //遍历与左点u的相邻的右点v
if (mp[u][v] && !vis[v]){ //若u、v相连,且v不在交替路中
vis[v] = true; //将v加入交替路
if (!match[v] || dfs(match[v])){ //若v未匹配或者v的匹配点存在增广路(递归),即找到增广路
match[v] = u; //修改匹配
return true;
}
}
return false;
}
int main()
{
freopen("data.in", "r", stdin);
int k;
while (scanf("%d", &k) && k){
int u, v, cnt = 0;
memset(mp, false, sizeof(mp));
memset(match, 0, sizeof(match));
scanf("%d %d", &n, &m);
while (k--){
scanf("%d %d", &u, &v);
mp[u][v] = true;
}
//依次给每一个左点找匹配点(增广路),若找到,cnt++
//这里隐含一个事实:在给当前点找匹配点的过程中,不会更改其他点的匹配状态
//即cnt的变化只取决于当前点是否找到匹配点
for (int i=1; i<=n; i++){
memset(vis, false, sizeof(vis));
if (dfs(i)) cnt++;
}
printf("%d\n", cnt);
}
return 0;
}
二分图最优匹配(KM算法)
#include <bits/stdc++.h>
using namespace std;
const int N = 307;
bool visx[N], visy[N];
int n, a[N], b[N], w[N][N], match[N];
//a[]、b[]分别为左点和右点的标号,w[i][j]为i到j的权,
//match[i] = j 表示右点i与左点j匹配
bool dfs(int u){ //给u找匹配
visx[u] = true;
for (int v = 1; v <= n; v++)
if (!visy[v] && a[u] + b[v] == w[u][v]){
//满足 a[u] + b[v] == w[u][v] 才能加入
visy[v] = true;
if (!match[v] || dfs(match[v])){
match[v] = u;
return true;
}
}
return false;
}
int KM(){
//初始化a[] = max, b[] = 0, match[] = 0
memset(b, 0, sizeof(b));
for (int u = 1; u <= n; u++){
a[u] = w[u][1];
for (int v = 2; v <= n; v++)
a[u] = max(a[u], w[u][v]);
}
memset(match, 0, sizeof(match));
//给每个左点找最佳匹配
for (int u = 1; u <= n; u++){
while (1){ //可能需要多次才能找到
memset(visx, false, sizeof(visx));
memset(visy, false, sizeof(visy));
if (dfs(u)) break; //若找到匹配,就找下一个
//若未找到匹配, 就计算d值
int d = 0x7fffffff;
for (int i = 1; i <= n; i++) if (visx[i])
//本轮匹配涉及到的左点
for (int j = 1; j <= n; j++) if (!visy[j])
//本轮匹配未涉及到的右点
d = min(d, a[i] + b[j] - w[i][j]);
//修改标号,左点减、右点加
for (int i = 1; i <= n; i++)
if (visx[i]) a[i] -= d;
for (int i = 1; i <= n; i++)
if (visy[i]) b[i] += d;
}
}
//统计各匹配的权值之和
int ret = 0;
for (int v = 1; v <= n; v++)
ret += w[match[v]][v];
return ret;
}
int main()
{
freopen("data.in", "r", stdin);
while (~scanf("%d", &n)) {
memset(w, 0, sizeof(w));
for (int u = 1; u <= n; u++)
for (int v = 1; v <= n; v++)
scanf("%d", &w[u][v]);
printf("%d\n", KM()); //KM()返回最佳匹配的权值之和
}
return 0;
}