ACM刷题之路(十六)Acm程序设计竞赛自制模板(二)

简介: ACM刷题之路(十六)Acm程序设计竞赛自制模板

最短路1. Dijkstra算法

1. hdu 2544 最短路贪心手法  先把除1以外的距离初始化为无穷大
2. 先从1开始 利用1可以连接到其他顶点的边 更新距离数组d[],把1确定下来  1到1距离为0已固定
3. 在取非固定点的最小值 同样利用这个点可以连接到其他顶点的边 更新距离数组d[]...... 以此类推
4. 这样循环n次  即可求出1到n的最短路径  如果不连通  当某一个find函数返回-1  即可判断该图不连通
5. #include<iostream>
6. #include<algorithm>
7. #include<vector>
8. using namespace std;
9. int n, m;
10. bool b[102];//判断最短距离是否确定, 已确定为true 否则为假
11. int d[102];//保存1到各个节点的距离 若b数组相应值为true 则必为最短路 否则为临时最短路
12. #define inf 10000000
13. int find()//找到所有距离的最小值
14. {
15.   int i, j = -1, min = inf;
16.   for (i = 1; i <= n; i++)
17.   {
18.     if (b[i]==false && d[i] < min)
19.     {
20.       j = i;
21.       min = d[i];
22.     }
23.   }
24.   return j;
25. }
26. int main()
27. {
28.   int i, j;
29.   int q, w, e;
30.   while (cin >> n >> m)
31. //输入点数n  和 边数 m
32.   {
33.     vector<pair<int, int> >v[102];
34. //用vector + pair 模拟二维数组 即三个变量的对应关系
35.     if (n == 0 && m == 0) break;
36.     while (m--)
37.     {
38.       pair<int, int>p;//pair为辅助容器
39.       scanf("%d%d%d", &q, &w, &e);
40.       p.first = w;
41.       p.second = e;
42.       v[q].push_back(p);
43. // q 到 w 的 边长 为 e
44.       p.first = q;
45.       v[w].push_back(p);
46. // w 到 q 的 边长 为 e
47.     }
48.     fill(b, b + 102, false); 
49. //初始化函数  节点全部初始化为未访问
50.     fill(d, d + 102, inf);
51. //距离全部初始化为无穷大
52.     d[1] = 0; // 1到1的距离为0
53.     for (i = 1; i <= n; i++)
54.     {
55.       j = find();
56.       b[j] = true;
57. for (vector<pair<int, int> >::iterator it = v[j].begin(); it != v[j].end(); it++)
58. {
59. //此循环为对j到其他结点遍历  更新最短边
60. if (d[it->first] > d[j] + it->second)//如果可以通过中间点使路变的更短
61. {
62. d[it->first] = d[j] + it->second;
63. }
64.   }
65. }
66.     cout << d[n] << endl;
67. //输出1到n的最短距离 
68.   }
69.   return 0;
70. }
71. 
72. 测试数据
73. 9 15
74. 1 2 6
75. 1 3 3
76. 1 4 1
77. 2 3 2
78. 3 4 2
79. 4 5 6
80. 2 5 1
81. 4 6 10
82. 5 6 4
83. 5 7 3
84. 5 9 6
85. 5 8 2
86. 8 9 3
87. 6 7 2
88. 7 9 4
89. 答案:11

最短路2. Floyd算法

1. hdu 2544 最短路 Floyd算法 总结
2. 
3. 状态  :Accepted  31MS  1848K
4. 基本思想:点A到点B无非是A直接到B 和 A通过若干个点再到B 这两种可能
5. 对于每一个点 都当一遍中间点 判断a[j][i] + a[i][k] < a[j][k]
6. 最后的a数组即可表示最短路径  a[i][j]代表i到j的最短路径 
7. 
8. 
9. 时间复杂度为o(n^3) 
10. 
11. 
12. #include<iostream>
13. #include<algorithm>
14. #include<vector>
15. using namespace std;
16. int n, m;
17. int a[105][105];
18. //a数组直接保存每个点之间的最短距离
19. #define inf 10000000
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
28. 
29. 
30. 
31. 
32. 
33. int main()
34. {
35.   int i, j, k;
36.   int q, w, e;
37.   while (cin >> n >> m)
38. //输入点数n  和 边数 m
39.   {
40.     if (n == 0 && m == 0) break;
41.     for (i = 0; i < 105; i++)
42. //全部初始化为无穷大
43.     {
44.       for (j = 0; j < 105; j++)
45.       {
46.         a[i][j] = inf;
47.       }
48.     }
49.     while (m--)
50.     {
51.       scanf("%d%d%d", &q, &w, &e);
52.       a[q][w] = e;//q到w的距离为e
53.       a[w][q] = e;//w到q的距离为e
54.     }
55. //Floyd算法 o(n^3) 暴力循环判断点i是否可以作为中间点
56.     for (i = 1; i <= n; i++)
57.     {
58.       for (j = 1; j <= n; j++)
59.       {
60.         for (k = 1; k <= n; k++)
61.         {
62. if (a[j][i] + a[i][k] < a[j][k])
63. //如果i当中间点可以缩短路径长度
64. {
65.   a[j][k] = a[j][i] + a[i][k];
66. }
67.         }
68.       }
69.     }
70.     cout << a[1][n] << endl;
71. //输出1到n的最短距离 
72.   }
73.   return 0;
74. }

最小生成树prim算法

1. /*
2. 算法思路:
3. 首先就是从图中的一个起点a开始,把a加入U集合,然后,寻找从与a有关联的边中,
4. 权重最小的那条边并且该边的终点b在顶点集合:(V-U)中,我们也把b加入到集合U中,
5. 并且输出边(a,b)的信息,这样我们的集合U就有:{a,b},
6. 然后,我们寻找与a关联和b关联的边中,权重最小的那条边并且该边的终点在集合:
7. (V-U)中,我们把c加入到集合U中,并且输出对应的那条边的信息,这样我们的集合U就有:
8. {a,b,c}这三个元素了,一次类推,直到所有顶点都加入到了集合U。
9. 2018/12/6   最小生成树   Prim算法
10. */
11. #include<iostream>
12. #include<algorithm>
13. #include<cstring>
14. using namespace std;
15. const int inf = 10000000;
16. const int N = 1002;
17. int G[N][N];//G数组 保存 边长
18. int d[N];
19. //d数组  保存所有点离树的最短距离
20. int n, m;
21. bool vis[N];
22. // vis数组 保存 判断这个点是否放入树中 
23. int prim()
24. {
25.   fill(d, d + N, inf);//初始化无穷大
26.   d[1] = 0;
27.   int ans = 0;//初始化答案为0
28. 
29. 
30. 
31. 
32. 
33.   for (int i = 1; i <= n; i++)
34. //i层循环 找所有连通到的边中最短的
35.   {
36.     int u = -1;//找单次的最短边
37.     int min = inf;
38.     for (int j = 1; j <= n; j++)
39.     {
40.       if (vis[j] == false && d[j]<min)//遍历求出 没有进入树的节点 离树的最短距离
41.       {
42.         u = j;
43.         min = d[j];
44.       }
45.     }
46.     if (u == -1)//如果所有边都无穷大,说明不连通,就返回错误
47.       return -1;
48.     vis[u] = true;//该点入树
49.     ans += d[u];//距离总和加上U点离树的距离
50.     for (int v = 1; v <= n; v++)
51.     {//这里是更新没有入树的节点离树的最短距离
52.       if (vis[v] == false && G[u][v] != inf&&G[u][v]<d[v])
53.         d[v] = G[u][v];
54.     }
55.   }
56.   return ans;
57. }
58. int main()
59. {
60.   int u, v, c;
61.   cin >> n >> m;
62.   fill(G[0], G[0] + N*N, inf);
63.   for (int i = 1; i <= m; i++)
64.   {
65.     cin >> u >> v >> c;
66.     G[u][v] = G[v][u] = c;
67.   }
68.   int ans = prim();
69.   if (ans == -1)//如果不连通
70.     cout << "-1" << endl;
71.   else
72.     cout << ans << endl;
73.   return 0;
74. }

凸包

1. #include<iostream>
2. #include<cstdio>
3. #include<cstring>
4. #include<algorithm>
5. #include<cmath>
6. #define PI 3.1415926535
7. using namespace std;
8. struct node
9. {
10.   int x, y;
11. };
12. node vex[1000];//存入的所有的点
13. node stackk[1000];//凸包中所有的点
14. int xx, yy;
15. bool cmp1(node a, node b)//排序找第一个点
16. {
17.   if (a.y == b.y)
18.     return a.x<b.x;
19.   else
20.     return a.y<b.y;
21. }
22. int cross(node a, node b, node c)//计算叉积
23. {
24.   return (b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y);
25. }
26. double dis(node a, node b)//计算距离
27. {
28.   return sqrt((a.x - b.x)*(a.x - b.x)*1.0 + (a.y - b.y)*(a.y - b.y));
29. }
30. bool cmp2(node a, node b)//极角排序另一种方法,速度快
31. {
32.   if (atan2(a.y - yy, a.x - xx) != atan2(b.y - yy, b.x - xx))
33.     return (atan2(a.y - yy, a.x - xx))<(atan2(b.y - yy, b.x - xx));
34.   return a.x<b.x;
35. }
36. bool cmp(node a, node b)//极角排序
37. {
38.   int m = cross(vex[0], a, b);
39.   if (m>0)
40.     return 1;
41.   else if (m == 0 && dis(vex[0], a) - dis(vex[0], b) <= 0)
42.     return 1;
43.   else return 0;
44. }
45. int main()
46. {
47.   int t, L;
48.   while (~scanf("%d", &t), t)//总共t个点
49.   {
50.     int i;
51.     for (i = 0; i<t; i++)
52.     {
53.       scanf("%d%d", &vex[i].x, &vex[i].y);//输入每个点的x和y坐标
54.     }
55.     if (t == 1)//如果只有一个点 凸包周长为0
56.       printf("%.2f\n", 0.00);
57.     else if (t == 2)
58.       printf("%.2f\n", dis(vex[0], vex[1]));//如果只有两个点 周长是两点距离
59.     else
60.     {
61.       memset(stackk, 0, sizeof(stackk));
62.       sort(vex, vex + t, cmp1);//排序找第一个点
63.       stackk[0] = vex[0];//用数组模拟栈
64.       xx = stackk[0].x;
65.       yy = stackk[0].y;
66.       sort(vex + 1, vex + t, cmp2);//极角排序
67.       stackk[1] = vex[1];//将凸包中的第两个点存入凸包的结构体中
68.       int top = 1;//最后凸包中拥有点的个数
69.       for (i = 2; i<t; i++)
70.       {
71.         while (i >= 1 && cross(stackk[top - 1], stackk[top], vex[i])<0)
72.           //i>=1防止越界  如果在右边 把栈顶元素出栈
73.           top--;
74.         stackk[++top] = vex[i];//控制<0或<=0可以控制重点,共线的,具体视题目而定。
75.       }
76.       double s = 0;
77.       for (i = 1; i <= top; i++)   //计算凸包的周长
78.         s += dis(stackk[i - 1], stackk[i]);
79.       s += dis(stackk[top], vex[0]);//最后一个点和第一个点之间的距离
80.       printf("%.2lf\n", s);
81.     }
82.   }
83. }

深搜dfs

1. void dfs(ll shu, int fei0, int changdu)//在搜索时需变化的量
2. {
3.  //满足条件返回
4.  //越界返回 
5.  //明显不符合条件返回 如距离奇偶性
6.  //已经找到目标  返回
7.  for (int i = 1; i <= 9; i++)
8.  {
9.    dfs(shu * 10 + i, fei0 + 1, changdu + 1);//继续深搜下去
10.   }
11. }

广搜bfs

1. void bfs(int x)
2. {
3.  queue<int>q1;//1.先创建队列
4.  q1.push(x);//2.放入初始条件
5.  while (!q1.empty())//3.开始循环广搜
6.  {
7.    int t = q1.front();
8.    q1.pop();//4.顶部删掉
9.    for (vector<int>::iterator it = v[t].begin(); it != v[t].end(); it++)
10.     {//5.把顶部能牵连到的元素压入队列
11.       if (a[*it] == false)
12.         q1.push(*it);
13.     }
14.   }//6.全部循环结束后,如果需要返回值就返回
15. }

母函数或多重背包

1. #include<iostream>
2. #include<cstring>
3. using namespace std;
4. struct node
5. {
6.  int fen, num;
7. };
8. int dp[41];
9. int main()
10. {
11.   int T;
12.   scanf("%d", &T);
13.   while (T--)
14.   {
15.     node a[10];
16.     memset(dp, 0, sizeof(dp));
17.     int n,  m;
18.     scanf("%d%d", &m, &n);
19.     int i, j, w;
20.     for (i = 0; i < n; i++)
21.       scanf("%d%d", &a[i].fen, &a[i].num);//输入每种物品的价值 和 物品数量
22.     dp[0] = 1;//初始化dp数组
23.     for (i = 0; i < n; i++)//枚举物品种类
24.     {
25.       for (j = m; j >= a[i].fen; j--)//枚举背包容量,01背包,要从大到小推,不然会重复加
26.       {
27.         for (w = 1; w <= a[i].num; w++)
28.     //枚举这个物品的个数,如果当前枚举到的价值能放入此物品的话,就加上之前的价值
29.         {
30.           if (j >= a[i].fen*w)
31.     //a[i].fen*w是w个物品能产生的价值,然后j-a[i].fen*w就是去掉a[i].fen*w的价值,加起来递推
32.           {
33.             dp[j] += dp[j - a[i].fen*w];
34.           }
35.           else break;
36.         }
37.       }
38.     }
39.     printf("%d\n", dp[m]);//m个物品的时候所能产生的价值
40.   }
41.   return 0;
42. }

01背包

1. for (i = 1; i <= n; i++)
2. {
3.  for (j = v; j >= a[i].cost; j--)
4.  {
5.    if (f[j] < f[j - a[i].cost] + a[i].val)
6.      f[j] = f[j - a[i].cost] + a[i].val;
7.  }
8. }

完全背包

1. for (int i = 1; i <= n; i++)
2. {
3.  for (int j = w[i]; j <= v; j++)
4.  {
5.    dp[j] = min(dp[j], dp[j - w[i]] + p[i]);
6.  }
7. }

多重背包

1. for (int i = 0; i<n; i++)
2. {      
3.  for (int k = 0; k<num[i]; k++)
4.  {            
5.    for (int j = cc; j >= v[i]; j--)
6.    {   
7.      dp[j] = max(dp[j], dp[j - v[i]] + v[i]);
8.    }
9.  }
10. }

状压DP

1. #include <iostream>
2. #include <string>
3. #include <cstring>
4. #include <stack>
5. #include <algorithm>
6. using namespace std;
7. 题意:有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小
8. 思路:因为最多只有15门课程,可以使用二进制来表示所有完成的状况
9. 例如5,二进制位101,代表第一门和第三门完成了,第二门没有完成,那么我们可以枚举1~1<<n便可以得出所有的状态
10. 然后对于每一门而言,其状态是t = 1<<i,我们看这门在现在的状态s下是不是完成,可以通过判断s&t是否为1来得到
11. 当得出t属于s状态的时候,我们便可以进行DP了,在DP的时候要记录路径,方便之后的输出
12. const int inf = 1 << 30;
13. struct node
14. {
15.   string name;
16.   int dead, cost;
17. } a[50];
18. struct kode
19. {
20.   int time, score, pre, now;
21. } dp[1 << 15];
22. int main()
23. {
24.   int t, i, j, s, n, end;
25.   cin >> t;
26.   while (t--)
27.   {
28.     memset(dp, 0, sizeof(dp));
29.     cin >> n;
30.     for (i = 0; i<n; i++)
31.       cin >> a[i].name >> a[i].dead >> a[i].cost;//每门课的名字、最晚完成时间、花费时间
32.     end = 1 << n;//n-1指的是每门课都完成的情况
33.     for (s = 1; s<end; s++)//暴力遍历
34.     {
35.       dp[s].score = inf;//先初始化为无穷大    是最少扣的分数
36.       for (i = n - 1; i >= 0; i--)
37.       {
38.         int tem = 1 << i;//右往左数第i+1位数
39.         if (s & tem)//如果右往左数第i+1位数为1   即i+1事件已经完成
40.         {
41.           int past = s - tem;//不可能为负 past代表当前事件发生之前的状态
42.           int st = dp[past].time + a[i].cost - a[i].dead;
43. //转移方程 要花的时间=前面要的时间+当前花的时间-最晚时间
44.           if (st<0)//如果当前不会被扣分 则等于0
45.             st = 0;
46.           if (st + dp[past].score<dp[s].score)//更新取最小值
47.           {
48.             dp[s].score = st + dp[past].score;//当前要扣的分
49.             dp[s].now = i;//当前dp最后执行的事件位置
50.             dp[s].pre = past;//执行前的二进制状态
51.             dp[s].time = dp[past].time + a[i].cost;
52.           }
53.         }
54.       }
55.     }
56.     stack<int> S;
57.     int tem = end - 1;
58.     cout << dp[tem].score << endl;//输出总的最少扣分
59.     while (tem)
60.     {
61.       S.push(dp[tem].now);//最后做的事件下表 入栈
62.       tem = dp[tem].pre;//转到前面的事件
63.     }
64.     while (!S.empty())
65.     {
66.       cout << a[S.top()].name << endl;
67.       S.pop();
68.     }
69.   }
70.   return 0;
71. }

线段树

1. #include<iostream>
2. #include<cstring>
3. using namespace std;
4. int n, p, a, b, m, x, y, ans;
5. struct node
6. {
7.  int l, r, w, f;
8. }tree[400001];
9. inline void build(int k, int ll, int rr)//建树 
10. {
11.   tree[k].l = ll, tree[k].r = rr;
12.   if (tree[k].l == tree[k].r)
13.   {
14.     scanf("%d", &tree[k].w);
15.     return;
16.   }
17.   int m = (ll + rr) / 2;
18.   build(k * 2, ll, m);
19.   build(k * 2 + 1, m + 1, rr);
20.   tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
21. }
22. inline void down(int k)//标记下传 
23. {
24.   tree[k * 2].f += tree[k].f;
25.   tree[k * 2 + 1].f += tree[k].f;
26.   tree[k * 2].w += tree[k].f*(tree[k * 2].r - tree[k * 2].l + 1);
27.   tree[k * 2 + 1].w += tree[k].f*(tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1);
28.   tree[k].f = 0;
29. }
30. inline void ask_point(int k)//单点查询
31. {
32.   if (tree[k].l == tree[k].r)
33.   {
34.     ans = tree[k].w;
35.     return;
36.   }
37.   if (tree[k].f) down(k);
38.   int m = (tree[k].l + tree[k].r) / 2;
39.   if (x <= m) ask_point(k * 2);
40.   else ask_point(k * 2 + 1);
41. }
42. inline void change_point(int k)//单点修改 
43. {
44.   if (tree[k].l == tree[k].r)
45.   {
46.     tree[k].w += y;
47.     return;
48.   }
49.   if (tree[k].f) down(k);
50.   int m = (tree[k].l + tree[k].r) / 2;
51.   if (x <= m) change_point(k * 2);
52.   else change_point(k * 2 + 1);
53.   tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
54. }
55. inline void ask_interval(int k)//区间查询 
56. {
57.   if (tree[k].l >= a&&tree[k].r <= b)
58.   {
59.     ans += tree[k].w;
60.     return;
61.   }
62.   if (tree[k].f) down(k);
63.   int m = (tree[k].l + tree[k].r) / 2;
64.   if (a <= m) ask_interval(k * 2);
65.   if (b>m) ask_interval(k * 2 + 1);
66. }
67. inline void change_interval(int k)//区间修改 
68. {
69.   if (tree[k].l >= a&&tree[k].r <= b)
70.   {
71.     tree[k].w += (tree[k].r - tree[k].l + 1)*y;
72.     tree[k].f += y;
73.     return;
74.   }
75.   if (tree[k].f) down(k);
76.   int m = (tree[k].l + tree[k].r) / 2;
77.   if (a <= m) change_interval(k * 2);
78.   if (b>m) change_interval(k * 2 + 1);
79.   tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
80. }
81. int main()
82. {
83.   printf("请输入线段树的大小n\n");
84.   scanf("%d", &n);//n个节点 
85.   printf("请输入n个数,分别是1到n的初始值\n");
86.   build(1, 1, n);//从1开始做下标 ,建立1到n的线段树
87.   printf("建树完成\n请输入操作线段树的次数m\n");
88.   scanf("%d", &m);//m种操作 
89.   printf("请输出相应指令\n1 单点查询\n2 单点修改\n3区间查询\n4区间修改\n");
90.   for (int i = 1; i <= m; i++)
91.   {
92.     scanf("%d", &p);
93.     ans = 0;
94.     if (p == 1)
95.     {
96.       printf("单点查询,输出第x个数 \n请输入x\n");
97.       scanf("%d", &x);
98.       ask_point(1);//单点查询,输出第x个数 
99.       printf("第%d个数是%d\n", x, ans);
100.    }
101.    else if (p == 2)
102.    {
103.      printf("对第x个数加上y,请输入X和Y\n");
104.      scanf("%d%d", &x, &y);
105.      change_point(1);//单点修改
106.      printf("修改完成\n");
107.    }
108.    else if (p == 3)
109.    {
110.      printf("查询[a,b]区间的和,请输入a和b\n");
111.      scanf("%d%d", &a, &b);//区间查询 
112.      ask_interval(1);
113.      printf(" %d 到 %d 的和为 %d\n", a, b, ans);
114.    }
115.    else
116.    {
117.      printf("对第[a,b]中所有数都加上y,请输入a,b和y\n");
118.      scanf("%d%d%d", &a, &b, &y);//区间修改 
119.      change_interval(1);
120.      printf("修改完成\n");
121.    }
122.  }
123. }

共计27个算法/数据结构   加  三块头文件函数

相关文章
|
10月前
ACM刷题之路(十六)Acm程序设计竞赛自制模板(一)
ACM刷题之路(十六)Acm程序设计竞赛自制模板
|
10月前
|
Java C语言 C++
ACM刷题之路(二)谈谈我对ACM的理解
ACM刷题之路(二)谈谈我对ACM的理解
|
7月前
|
存储 算法 数据安全/隐私保护
【强烈推荐收藏】坚持3个月爆肝华为机试108题C++全解(适合新手入门,就业必刷套题)
【强烈推荐收藏】坚持3个月爆肝华为机试108题C++全解(适合新手入门,就业必刷套题)
165 2
|
7月前
|
监控 安全 中间件
CTF/AWD竞赛标准参考书+实战指南:《AWD特训营》
CTF/AWD竞赛标准参考书+实战指南:《AWD特训营》
|
10月前
|
Java Android开发
ACM刷题之路(七)字符串处理 记元培ACM院赛
ACM刷题之路(七)字符串处理 记元培ACM院赛
|
算法 C++
深入浅出程序设计竞赛(基础篇)
本书从编程语言开始,以C++语言为基础讲解, 然后讲解了基础的算法开始,基本上常用的算法都介绍了, 还进行了基础的数学与数论知识。
1184 0
深入浅出程序设计竞赛(基础篇)
|
JavaScript 前端开发
【蓝桥杯Web】2022年第十三届蓝桥杯Web大学组省赛真题解析(精华版)
【蓝桥杯Web】2022年第十三届蓝桥杯Web大学组省赛真题解析(精华版)
【蓝桥杯Web】2022年第十三届蓝桥杯Web大学组省赛真题解析(精华版)
|
Rust JavaScript 安全
2021年,快速Deno上手指南 | 🏆 技术专题第九期征文
2021年,快速Deno上手指南 | 🏆 技术专题第九期征文
173 0
2021年,快速Deno上手指南 | 🏆 技术专题第九期征文
第五天自己的笔记云
以后自己也试试做一个自己的笔记云
525 0
第五天自己的笔记云
|
新零售 运维 开发者
混沌工程初体验,读书好礼等你拿——MVP一周精选 20200612
本周凯京科技运维负责人郭旭东带来混沌工程及混沌工程实验工具 ChaosBlade的介绍,驻云科技运维总监乔锐杰读书汇也正式启动,一起学习《阿里云运维架构实践秘籍》。更多精彩,迅速查看。
混沌工程初体验,读书好礼等你拿——MVP一周精选 20200612