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个算法/数据结构   加  三块头文件函数

相关文章
|
6月前
|
Java
杨老师课堂_Java教程第五篇之函数运用
杨老师课堂_Java教程第五篇之函数运用
37 1
|
6月前
|
机器学习/深度学习 C++
程序与技术分享:2017ACM
程序与技术分享:2017ACM
33 0
ACM刷题之路(十六)Acm程序设计竞赛自制模板(一)
ACM刷题之路(十六)Acm程序设计竞赛自制模板
|
Java C语言 C++
ACM刷题之路(二)谈谈我对ACM的理解
ACM刷题之路(二)谈谈我对ACM的理解
126 0
|
设计模式 缓存 Java
又快又稳!Alibaba出品Java性能优化高级笔记(全彩版)震撼来袭
性能优化 作为一个程序员,性能优化是常有的事情,不管你是刚入行的小白还是已经入坑了很久的小秃头都会经历很多不同层次的性能优化——小到代码审查大到整个系统设计的优化!大势所趋之下,如何让自己的优化方向精准到性能瓶颈的那个点以及尽可能的提高优化的性价比已经慢慢成为每一个程序员都要考虑的问题了~ 下面是目前程序员进行性能优化时需要遵循的一些原则以及注意的一些点,大家可以看看自己在进行优化的时候是否有考虑到这些:
|
Web App开发 Rust 前端开发
前端周刊第十九期
前端周刊发表每周前端技术相关的大事件、文章教程、一些框架的版本更新、以及代码和工具。每周定期发表,欢迎大家关注、转载。
前端周刊第十九期
|
存储 缓存 网络协议
学长笔记开源了!
下面的笔记来源于读者(B站:车干学长)学习图解网络时的笔记,他也补充了一些我没写到的点,比如强制缓存/协商缓存之类的知识。 全文共 1w 字,干货满满!发车!
学长笔记开源了!
|
安全 NoSQL 机器人
准网安研究生の服务器初体验
小菜鸡第一次使用云服务器的体验
|
移动开发 前端开发 JavaScript
xy哥怒肝,前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!
xy哥怒肝,前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!
|
机器学习/深度学习 人工智能 自然语言处理