[bzoj4398] 福慧双修 最短路 二进制分组
~~~题面~~~
题解:
考场上看的这道题,,,当时70分算法打挂了,今天才知道这个也是原题。。。。
首先,对于不跟1相邻的边,肯定不会经过两次,因为经过两次就回来了,除了增加路径长度之外没有任何意义。
但是跟1相邻的边是可能会经过2次的,因为虽然增加了路径长度,但这次回来就直接到终点了,所以完全可能重复走。
那么有一个很容易发现的思路是,我们可以强制某一条边是出边(即从1点出去可以走的边),其他边则为入边,这样的话就可以强制走不同的路了,但这样时间复杂度较大。考虑优化它。
可以发现,这样分组的本质就是要使得对于任意二元组(x, y)而言,x和y都至少要有两次被分配在不同的集合中,这样它们才可以互相搭配组成两条可能的路径。
为什么是两条呢?
对于相同的路径而言,分两个方向走一遍权值是不同的,也就是说对于这条路径:1 --- 2 --- 4 --- 3 --- 1,我既可以1 ---> 2 ---> 4 ---> 3 ---> 1,也可以1 <--- 2 <--- 4 <--- 3 <--- 1.如果只是单纯的把2,3两条边分在不同的集合当中,你根本不知道会找到哪条路径,也不知道是否这条路径刚好就是最优的那条。
观察到任意边的编号都是不同的,这意味这它们对应的二进制串至少有一位是不同的,所以我们可以枚举位数,按照当前位是0还是1给边分组,那么由于任意两个串对于的二进制串都至少有一位不同,因此它们一定会有一次被分在不同的集合当中。因为我们需要找到所有可能路径,所以要把当前位是0的分给s1和当前位是0的分给s2都试一遍才能保证正确性。
但实际上你会发现不用试2遍也可以过这道题,这是数据原因,,,因为我已经把我原来那份代码给hack掉了。。。。
因为你可以发现,会发生错误的几率是很低的,因为发生错误当且仅当对应的最短路径没有被找到,而这种情况出现在1号点对应的出边和入边的编号刚好所有不同的地方都是1 对 0或者0 对1,并且刚好那个1 对 0(0对1)就会将2条边分在错误的集合。
所以除非构造数据来卡,不然出现错误的可能性是很低的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define LL long long 5 #define AC 50100 6 #define ac 401000 7 8 int n, m, t, ans = INT_MAX, k; 9 int dis[AC], s[ac], top; 10 int Head[AC], date[ac], Next[ac], len[ac], tot = 1; 11 bool vis[AC]; 12 13 struct road{ 14 int x, y, dis1, dis2; 15 }way[ac]; 16 17 struct node{ 18 int dis, id; 19 }; 20 21 struct cmp{ 22 bool operator () (node a, node b) 23 { 24 return a.dis > b.dis; 25 } 26 }; 27 priority_queue<node, vector<node>, cmp> q; 28 29 inline int read() 30 { 31 int x = 0;char c = getchar();bool z = false; 32 while(c > '9' || c < '0') 33 { 34 if(c == '-') z = true; 35 c = getchar(); 36 } 37 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 38 if(!z) return x; 39 else return -x; 40 } 41 42 inline void upmin(int &a, int b) 43 { 44 if(b < a) a = b; 45 } 46 47 inline void upmax(int &a, int b) 48 { 49 if(b > a) a = b; 50 } 51 52 inline void add(int f, int w, int S) 53 { 54 if(w == 1) w = t; 55 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S; 56 //printf("%d ---> %d : %d\n", f, w, S); 57 } 58 59 void pre() 60 { 61 n = read(), m = read(), t = n + 1; 62 for(R i = 1; i <= m; i ++) 63 { 64 way[i].x = read(), way[i].y = read(), way[i].dis1 = read(), way[i].dis2 = read(); 65 if(way[i].x == 1 || way[i].y == 1) s[++top] = i; 66 } 67 } 68 69 void build(int x) 70 { 71 //printf("%d:\n", x); 72 int l = 1; 73 tot = 0; 74 memset(Head, 0, sizeof(Head)); 75 for(R i = 1; i <= m; i ++) 76 { 77 if(i == s[l]) 78 { 79 if(k ^ (l & x))//不仅仅要被分在不同集合当中, 80 {//还需要x1s1 + x2s2 ; x1s2 + x2x1两种情况都出现一次才能包括所有的情况 81 if(way[i].x == 1) add(way[i].x, way[i].y, way[i].dis1); 82 else add(way[i].y, way[i].x, way[i].dis2); 83 } 84 else 85 { 86 if(way[i].x == 1) add(way[i].y, way[i].x, way[i].dis2); 87 else add(way[i].x, way[i].y, way[i].dis1); 88 } 89 l ++; 90 } 91 else 92 { 93 add(way[i].x, way[i].y, way[i].dis1); 94 add(way[i].y, way[i].x, way[i].dis2); 95 } 96 } 97 } 98 99 void spfa() 100 { 101 int x, now; 102 memset(dis, 127, sizeof(dis)); 103 memset(vis, 0, sizeof(vis)); 104 dis[1] = 0; 105 q.push((node){0, 1}); 106 while(!q.empty()) 107 { 108 x = q.top().id, q.pop(); 109 while(vis[x] && !q.empty()) x = q.top().id, q.pop(); 110 if(vis[x]) break; 111 vis[x] = true; 112 for(R i = Head[x]; i; i = Next[i]) 113 { 114 now = date[i]; 115 if(dis[now] > dis[x] + len[i]) 116 { 117 dis[now] = dis[x] + len[i]; 118 q.push((node){dis[now], now}); 119 } 120 } 121 } 122 upmin(ans, dis[t]); 123 } 124 125 void work() 126 { 127 int tmp = 1; 128 for(R i = 0; i <= 17; i ++) 129 { 130 k = 1, build(tmp), spfa(); 131 k = 0, build(tmp), spfa(); 132 tmp <<= 1; 133 } 134 printf("%d\n", ans); 135 } 136 137 int main() 138 { 139 freopen("in.in", "r", stdin); 140 pre(); 141 work(); 142 fclose(stdin); 143 return 0; 144 }
View Code
转载于:https://www.cnblogs.com/ww3113306/p/9802823.html
[bzoj4398] 福慧双修 最短路 二进制分组相关推荐
- 【BZOJ2069】ZAW(POI2004)-最短路+二进制分组
测试地址:ZAW 题目大意: 给定一张边是双向的图,一条边走不同的方向可能代价不同,代价都非负,求从点111出发,不经过重复的点或边的最小回路.n≤5000,m≤10000n\le 5000,m\le ...
- hdu-6166(最短路+二进制分组)
题意:给你n个点m条边的有向图,然后再给你k个不同的点,问你这k个点的最小距离: 解题思路:这道题最需要注意的就是k个点一定是不同的,那么有一个结论就是任意两个不同的数字中,在他们的二进制地表示中,一 ...
- BZOJ2407:探险/BZOJ4398:福慧双修-最短路+分治
两道都是权限题- 题意: 给出一张n个点,m条边的图,同一条边不能走两次,每条边正着走与反着走所需要的时间可能不同,求一个从1开始的大于一个点的最短环 N<=10000,M<=200000 ...
- bzoj#2407-探险【最短路,二进制分组】
正题 题目链接:https://darkbzoj.tk/problem/2407 题目大意 nnn个点的一张无向图(但是正反权值不同),求一个从111出发回到111且不经过重复边的最短路径. 1≤n≤ ...
- 【技巧 二进制分组】bzoj4398: 福慧双修2407: 探险
二进制分组也可以说是一种比较优美的拆贡献方式吧? Description 菩萨为行,福慧双修,智人得果,不忘其本. --唐朠立<大慈恩寺三藏法师传> 有才而知进退,福慧双修,这才难得. - ...
- bzoj 4398 福慧双修——二进制分组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4398 如果枚举1号点走哪些点出去,就从那些点出发跑多源最短路即可.最短路不会重复经过一条边. ...
- [bzoj4398]福慧双修
[bzoj4398]福慧双修 要求不能走重复的边,路径就是1->u->v->1,然后我们可以i想到一个暴力:把1去掉 枚举u跑最短路然后枚举v更新答案.因为边权非负,这个肯定是合法的 ...
- bzoj29894170数列——二进制分组+主席树
题意的转化挺巧妙的 可以联想到曼哈顿距离! 并且,所谓的修改还要查询历史版本,并且修改之间不动只算一次,不就是给平面上加一个点吗? 看成(x,a[x])的点 就是一个菱形区域 转切比雪夫距离,变成矩形 ...
- 【BZOJ3821/UOJ46】玄学(二进制分组,线段树)
[BZOJ3821/UOJ46]玄学(二进制分组,线段树) 题面 BZOJ UOJ 题解 呜,很好的题目啊QwQ. 离线做法大概可以线段树分治,或者直接点记录左右两次操作时的结果,两个除一下就可以直接 ...
最新文章
- ADSL防御******的十大方法
- ES6箭头函数和模板字符串
- SAP Spartacus CmsService的CmsActions.LoadCmsComponent
- 【渝粤题库】国家开放大学2021春1398分析化学(本)题目
- 从0到1使用VUE-CLI3开发实战(五):模块化VUEX及使用vuetify
- 2020.10.s1 冯上
- SQL Server时间粒度系列----第4节季、年时间粒度详解
- 吴恩达深度学习课程第四章第二周编程作业(pytorch实现)
- C++程序设计原理与实践(第二版)思考题答案
- 高校实验室安全隐患及安全建设-LIMS2
- 扬州大学复试1301软件工程【自制题库个人复习用】
- 计算机键盘没有fn,键盘Fn键失灵怎么解决?键盘Fn键失灵的修复方法
- python九宫格拼图_利用Python实现朋友圈中的九宫格图片效果
- 使用file.delete删除文件不起作用解决办法
- 嵌入式行业技术思维导图
- Captcha Cracker (java)附带replace用法
- 【JY】西南交通大学柔性防护:边坡地质动力灾害柔性防护仿真
- SPSS多元线性回归残差分析的基本方法
- android ios 实时视频,Twitter推出适用于Android和iOS设备的实时视频
- 只要8元,就能体验美国第一夫人的乐趣