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;}
阅读全文
0 0
- NOIP初赛相关代码
- 【NOIP初赛】 网络相关
- 【NOIP初赛】 计算机硬件
- 【NOIP初赛】 杂碎知识点
- 【NOIP初赛】 组合数学
- [NOIP初赛知识点汇总]
- noip初赛冒个泡
- NOIP初赛总复习
- NOIP初赛知识
- 对于NOIP初赛感想
- *TEST 6 for NOIP + NOIP初赛
- NOIP初赛复习资料(全)
- NOIP初赛准备:第一课时
- NOIP初赛准备:第二课时
- NOIP初赛准备:第三课时
- NOIP初赛准备:第四课时
- 【NOIP初赛】 软件与操作系统
- NOIP初赛准备:第五课时
- linux基础练习(二)
- A
- HDU4049【状压DP】
- 面向逻辑概念的虚拟编程
- git学习
- NOIP初赛相关代码
- 代码问题汇总
- 通过Cisco Packet Tracer初步了解数据链路层
- CSDN的markdown编辑器
- Redis持久化之RDB
- Shiro安全框架的使用步骤
- 如何将web项目部署到tomcat中?
- ------------------------------------------------
- <10/10>打卡随笔