NOIP初赛相关代码

来源:互联网 发布:淘宝技巧 编辑:程序博客网 时间:2024/05/19 16:37

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;}