第六次新生排位赛 手写链表 树形dp 计数排序 状态压缩dp
来源:互联网 发布:淘宝运营是做什么的 编辑:程序博客网 时间:2024/06/03 20:47
第一题 这题据说有最小生成树和并查集两种方法。
看了几种别的同学的解法,没有看到用最小生成树的,都是并查集
其他同学的方法是找连通块,连通块的个数减一就是最少要修的路(当然建立连通块用的是并查集)
我的方法是:至少要修n-1条路
对连通的每条路进行判断,如果连接的两个点本来就联通的,这条路为非必须路,不计数。否则就是一条必须路。必须路一共要n-1条,减去已经有的,就是还需要修的
A. 修路 2014新生暑假个人排位赛06
题目描述
小弱的学校很喜欢修路,现在给你一张他学校的地图,地图上有n个点和m条双向边,每条边代表一条路,这条路有可能是畅通,也有可能正在修路。大家都知道修路使得交通很不方便。所有小弱很想学校快快的把路修好,使得他能够很轻松的到达主楼915去刷题。但考虑到学校的施工能力有限,小弱想让你帮他算出学校需要集中力量马上修好的最少路数,使得他能够从学校任意点出发,在不经过正在施工的路下到达主楼(编号为1)。
输入格式
有多组数据。
每组数据以n( 1<=n<=10000), m(1<=m<=200000)开头。接下来一行有m行数。每行有三个数,对应于u, v, s,分别为这条路的两端点(编号从1到n)和路况,s = 0代表畅通, s = 1 代表正在修路。输入保证图是连通图。
输出格式
对每组数据输出对应的最少路数。
输入样例
3 21 2 01 3 13 21 2 01 3 03 21 2 11 3 1
输出样例
102
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm> using namespace std; int father[10005]; int main(){ int n, m; while (~scanf("%d %d", &n, &m)) { for (int i=1; i<=n; i++) father[i] = i; int ans = 0; for (int i=1; i<=m; i++) { int u, v, s; scanf("%d %d %d", &u, &v, &s); if (s==0) { ans++; while (father[u]!=u) u = father[u]; while (father[v]!=v) v = father[v]; if (u==v) ans--; else father[v] = u; } } ans = n-1-ans; printf("%d\n", ans); }}
第二题
使用了状态压缩dp,当然递推式有很多种,我直接抄了小白书上的,用递推确实比用递归快了一点,而且内存用得少一些,不过个人感觉递归会比较好理解吧
dp[s][v] 表示的是以v为起点走完不在s内的所有点的一条路(最大或最小)权值。其中v在集合s内。
dp[s][v] = max(dp[s][v] , d[v][u] + dp[s | 1 << u][u] )
这个递推式是从大到小递推的,太难理解了(唉,抄别人的就是不好,要理解很长时间,要是我自己写肯定不会写这么深奥的)
所以对于本题dp[1<<i][i] 表示的是以i为起点走遍所有点的最大权值(显然此时s中只有i点)
445. 高兴
题目描述
小弱有n个玩具,现在小弱想把他们排列成一行,把玩具j放在玩具i的右边相邻的位置上,他将得到一个高兴值Hij.
输入格式
输入有多组数据。
每组数据以一个整数n(n <= 18)开头。
接下来n行, 每行n个整数。 第i行第j列Hij( Hij的绝对值 <= 10000)代表把玩具j放在玩具i的右边相邻的位置时高兴值。输入保证Hii = 0,最左边只考虑与右边相邻的玩具,最右边只考虑与左边相邻的玩具。
输出格式
对于每组数据输出最大高兴值。
输入样例
20 12 030 -1 73 0 33 3 0
输出样例
2 10
<pre name="code" class="cpp"><pre name="code" class="cpp">#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm>#define INF 1800000using namespace std; int n;int d[20][20];int dp[1 << 18][20];bool vis[1 << 18][20];//也可以dp设个初始值,不等于初始值就表示访问过了int rec(int s, int v){ if (vis[s][v] == true) return dp[s][v]; if (s==((1<<n)-1)) return dp[s][v] = 0; int res = -INF; for (int u=0; u<n; u++) if (!(s >> u & 1)) res = max(res, rec(s | 1 << u, u) + d[v][u]);// vis[s][v] = true; return dp[s][v] = res;}int main(){ while (~scanf("%d", &n)) { memset(vis, 0, sizeof vis); for (int i=0; i<n; i++) for (int j=0; j<n; j++) scanf("%d", &d[i][j]); int ans = -INF; for (int i=0; i<n; i++) ans = max(ans, rec(1<<i, i)); printf("%d\n", ans); } return 0;}
这种是递推
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm>#define INF 1800000using namespace std;int n;int d[20][20];int dp[1 << 18][20];int main(){ while (~scanf("%d", &n)) { for (int i=0; i<n; i++) for (int j=0; j<n; j++) scanf("%d", &d[i][j]); for (int s=0; s < 1<<n; s++) fill(dp[s], dp[s]+n, -INF); for (int i=0; i<n; i++) dp[(1<<n)-1][i] = 0;//加减号的优先级比左移运算符高 for (int s=(1<<n)-2; s>0; s--) for (int v=0; v<n; v++) if (s>>v & 1) for (int u=0; u<n; u++) if ((u!=v)&&(!(s>>u&1))) dp[s][v] = max(dp[s][v], dp[s|1<<u][u] + d[v][u]); //犯傻了。这里括号写错,主要是抄错了,写成了max(dp[s][v], dp[s|1<<u][u]) + d[v][u]; //调试得眼睛都花了,才找出来 int ans = -INF; for (int i=0; i<n; i++) { ans = max(ans, dp[1<<i][i]); } printf("%d\n", ans); } return 0;}
第三题
449. 排序
题目描述
给你n个数,请你将他们从小到大输出出来。
输入格式
多组数据。
输入第一行为n,接下来一行给出n个数,每个数在0到10000。
输入文件大小为8.2MB。
输出格式
输出一行,排序之后的n个数。
输入样例
34 2 1
输出样例
1 2 4
这题的解法除了计数排序外,是针对很多组短数据,如果是像我说的,数据量大,用排序的话不见得最优,当然可以加个判断,不同的数据是否够大
两种判断(不过在这题由于不是学长的考点,可能学长给的数据里没有那种很长很长数据范围遍布10000的,所以我比过了,没有速度上的优势)
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm>using namespace std;int a[10005];int b[10005];int n;int main(){ memset(a, 0, sizeof a); while (~scanf("%d", &n)) { int temp; int maxn = 0; int num = 0; int minn = 10000000; for (int i=0; i<n; i++) { scanf("%d", &temp); if (!a[temp]) { b[num++] = temp; minn = min(minn, temp); maxn = max(maxn, temp); } a[temp]++; } if (num < 2000) { sort(b, b+num); for (int j=0; j<num; j++) { while(a[b[j]]) { a[b[j]]--; n--; if (n==0) { printf("%d\n", b[j]); } else printf("%d ", b[j]); } } } else { for (int j=minn; j<=maxn; j++) { while(a[j]) { a[j]--; n--; if (n==0) { printf("%d\n", b[j]); } else printf("%d ", b[j]); } } } } return 0;}
针对学长这次考点的
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm> using namespace std; int a[10005];int b[10005];int n;int main(){ memset(a, 0, sizeof a); while (~scanf("%d", &n)) { int temp; int maxn = 0; int num = 0; for (int i=0; i<n; i++) { scanf("%d", &temp); if (!a[temp]) b[num++] = temp; a[temp]++; } sort(b, b+num); for (int j=0; j<num; j++) { while(a[b[j]]) { a[b[j]]--; n--; if (n==0) { printf("%d\n", b[j]); } else printf("%d ", b[j]); } } } return 0;}
第四题,我又忧伤的考虑复杂了,建棵树dfs遍历一遍就可以了,我还在一个劲地想有没有什么简便方法
由于我用的是vector 所以卡着时间过了
还是手写的比较好吧
444. 爱好和平
题目描述
在星际时代,每个帝国都靠着贸易路线连接着各个联盟星球,这些贸易路线都是双向可达的。一个帝国的综合实力由他贸易连接着的联盟星球数决定。
学姐作为Mays帝国的领袖,长期与Luke帝国保持着敌对关系,爱好和平的学姐希望结束长达几个世纪的战争,于是找实验室定做了一颗代号小苹果的炸弹,可以定点摧毁一颗星球,这颗星球被毁后,与它相连的全部贸易就都被切断了,这样Luke帝国可能就被切断为一个小联盟,他们就再也不会对学姐的地位构成威胁啦~
经过调查,Luke帝国为了节约经费,他的联盟星之间都有且仅有一条直接或间接的贸易通路。
现在给出Luke帝国的贸易线路,学姐想知道摧毁哪一颗行星可以使得分裂后的若干Luke联盟威胁最大的分部最小。
输入格式
输入有多组数据,组数不大于10组。每一组开头一行为n,m,表示Luke帝国的联盟星球数量,和贸易关系数,接下来m行,每行两个整数u,v,表示星球u,v之间存在直接的贸易路线,1<=u,v<=n,1<=n,m<=100000
输出格式
输出一个数表示推荐学姐摧毁的星球,如果有多解,输出编号最小的一个。
输入样例
5 41 21 31 44 5
输出样例
1
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm> using namespace std; vector <int> a[100003];int n, m;int minans, mini;int cal(int fomer, int i){ int s = 1; int help = 0; for (int j=0; j<a[i].size(); j++) { if (a[i][j]==fomer) continue; else { int tll = cal(i, a[i][j]); help = max(help, tll); s = s+tll; } } help = max(help, n-s); if ((help<minans)||((help==minans)&&(mini>i))) { minans = help; mini = i; } return s;}int main(){ while (~scanf("%d %d", &n, &m)) { for (int i=1; i<=n; i++) a[i].clear(); minans = 1e9; mini = 0; int u, v; for (int i=0; i<m; i++) { scanf("%d %d", &u, &v); a[u].push_back(v); a[v].push_back(u); } if (n==2) printf("%d\n", min(u, v)); else { cal(0, 1); printf("%d\n", mini); } } return 0;}
参考S的手写链表
#include <iostream>#include <cstdio>#include <string.h>#include <vector>#include <cmath>#include <algorithm>using namespace std;struct{ int to, next;}a[200005];//maxn<<1, 最大值的两倍int head[100005];int n, m, tot;int minans, mini;void add(int u, int v){ a[tot].to = v; a[tot].next = head[u]; head[u] = tot++; a[tot].to = u; a[tot].next = head[v]; head[v] = tot++;}int cal(int fomer, int u){ int s = 1; int help = 0; for (int j=head[u]; j!=-1; j = a[j].next)//手写链表a的结束标志在于head的初始化 { int v= a[j].to; if (v==fomer) continue; else { int tll = cal(u, v); help = max(help, tll); s = s+tll; // s用于统计所有子节点加上自己的节点个数 } } help = max(help, n-s); // help用于求子节点中节点数的最大,即除去它以后的最大分部 if ((help<minans)||((help==minans)&&(mini>u))) { minans = help; mini = u; } return s;}int main(){ while (~scanf("%d %d", &n, &m)) { memset(head, -1, sizeof head); minans = 1e9; //mini = 0; int u, v; for (int i=0; i<m; i++) { scanf("%d %d", &u, &v); add(u, v); } tot = 0; // 记得这里初始化 cal(0, 1); printf("%d\n", mini); } return 0;}
- 第六次新生排位赛 手写链表 树形dp 计数排序 状态压缩dp
- 状态压缩DP 树形D
- 状态压缩dp与树形dp
- 树形dp 和 状态压缩dp
- 【基于连通性的状态压缩DP】【NOI2007】生成树计数
- hdu 1438 钥匙计数之一(DP状态压缩)
- 【树形dp】连通块计数
- 状态压缩dp
- pku1038状态压缩dp
- 状态压缩DP 入门
- HDU1074 状态压缩DP
- Poj3254 状态压缩DP
- 状态压缩DP入门
- 【状态压缩DP】互不侵犯
- 【状态压缩DP】电子竞技
- HDU1074 状态压缩DP
- 【状态压缩DP】互不侵犯
- 状态压缩 DP AHU420
- poj 2528
- javaScript简单用户注册应用
- HDU 1495 非常可乐 BFS
- hdu 1920 Jackpot
- 【Untiy3D 游戏开发之一】Unity3D For Mac最新3.4.1版本破解教程
- 第六次新生排位赛 手写链表 树形dp 计数排序 状态压缩dp
- 码农创业记(三)
- 性能优化规则总结
- comet4j
- 开发Java应用要点
- 关于单击某一列显示到textbox中
- EasyBCD找回linux启动项
- HashMap循环遍历方式及其性能对比
- 某移动社交应用服务端架构浅析