HDU 6166 Senior Pan (思维枚举+最短路 求最近点对)
来源:互联网 发布:最好的文档扫描软件 编辑:程序博客网 时间:2024/06/06 17:52
Senior Pan
Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 898 Accepted Submission(s): 361
The task is simple : ZKC will give Pan a directed graph every time, and selects some nodes from that graph, you can calculate the minimum distance of every pair of nodes chosen in these nodes and now ZKC only cares about the minimum among them. That is still too hard for poor Pan, so he asks you for help.
Then m lines follow. Each line contains three integers
Then one line contains one integer K, the number of nodes that Master Dong selects out.1≤K≤n
The following line contains K unique integers
15 61 2 12 3 33 1 32 5 12 4 24 3 131 3 5
Case #1: 2
题意:给你一个n个点m条边的有向带边权的图。现在给你k个点,问k个点中最近的点对距离。 n, m, k <= 1e5
思路:
考虑到spfa可以求两个集合间的最短路径,(即初始将某个集合所有点入堆,dis = 0,直到找到另一个集合的第一个点结束)。
想到了这个问题我们就来想一下怎么去划分集合了,因为任意两点都要求一次,所以划分集合的方法必须要保证每两个点都至少一次被分到了不同的集合里去.
这里比较巧妙的一个方法就是 因为每个点都不相同,所以他们点的二进制状态中至少有一位不相同,那么我们就可以枚举二进制中的每一位,相同的分到一个集合,然后跑一次spfa维护最小值即可.因为n为1e5,所以最多17次就可以结束了.
另外一个需要注意的问题就是,该图是一个有向图,所以划分完集合后我们不确定是1集合到2集合最短,还是2集合到1集合最短,所以我们要正反跑两次spfa,一次1到2,一次2到1.维护一个最小值,就是答案了.
代码:
#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <queue>#include <vector>using namespace std;typedef long long ll;const ll INF = 1e18;const int maxn = 100005;int n, m, a[maxn], book[maxn], target[maxn];ll dis[maxn];struct node{ int to; ll w; node(){} node(int tt, ll ww) : to(tt), w(ww){}};queue<int> q;void init(){ memset(target, 0, sizeof(target)); memset(book, 0, sizeof(book)); for(int i = 1; i <= n; i++) dis[i] = INF; while(!q.empty()) q.pop();}vector<node> v[maxn];ll spfa(){ while(!q.empty()) { int u = q.front(); q.pop(); book[u] = 0; for(int i = 0; i < v[u].size(); i++) { int to = v[u][i].to; int w = v[u][i].w; if(dis[u]+w <= dis[to]) { dis[to] = dis[u]+w; if(!book[to]) { book[to] = 1; q.push(to); } } } } ll ans = INF; for(int i = 1; i <= n; i++) if(target[i]) ans = min(ans, dis[i]); return ans;}int main(){ int t, ca = 1; cin >> t; while(t--) { scanf("%d%d", &n, &m); for(int i = 0; i <= n; i++) v[i].clear(); int x, y, z, k; for(int i = 1; i <= m; i++) scanf("%d%d%d", &x, &y, &z), v[x].push_back(node(y, z)); scanf("%d", &k); for(int i = 1; i <= k; i++) scanf("%d", &a[i]); ll ans = INF; for(int i = 0; i <= 20; i++) { init(); for(int j = 1; j <= k; j++) { if(a[j]&(1<<i)) dis[a[j]] = 0, q.push(a[j]), book[a[j]] = 1; else target[a[j]] = 1; } ans = min(ans, spfa()); init(); for(int j = 1; j <= k; j++) { if(a[j]&(1<<i)) target[a[j]] = 1; else dis[a[j]] = 0, q.push(a[j]), book[a[j]] = 1; } ans = min(ans, spfa()); } printf("Case #%d: %lld\n",ca++,ans); } return 0;}
还可以随机划分集合, 转自点击打开链接
思路:
①我们在一个集合中找两个点显然有些难度,我们假设有一个起点集合,一个终点集合的话,我们建立一个超级源点连入起点集合的各个点,然后再建立一个超级汇点,将终点集合的各个点连入这个超级汇点的话,我们跑一遍从超级源点到超级汇点的最短路就很简单能够解决问题。
②但是我们现在没有这两个集合,我们不妨考虑将关键点集合分出两个集合,那么我们怎样分能够使得出错率最小呢?我们显然希望对半分。
所以我们随机这K个关键点的排列顺序,然后前半部分作为起点集合,后半部分作为终点集合去跑就行了。
③随机一次出来的结果出错率为3/4(正确的概率:起点分布正确的概率是1/2.终点分布正确的概率是1/2.相乘为1/4).两次都出错的概率为3/4*3/4.三次都出错的概率为3/4*3/4*3/4.依次类推,显然随机的次数越多,正确结果的概率越大。我们只需要其中任意一次正确即可。所以这样做的正确率是可以保证的。
所以我们随机20次左右就足够了,过程跑SPFA,维护最小解即可。
#include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; #define ll __int64 struct node { ll from,to,next,w; } e[150000]; ll cont,n,m,K,ss,tt,ans; ll xx[150000]; ll yy[150000]; ll ww[150000]; ll ned[150000]; ll head[150000]; ll dist[150000]; ll vis[150000]; void add(ll from,ll to,ll w) { e[cont].w=w; e[cont].to=to; e[cont].next=head[from]; head[from]=cont++; } void SPFA() { queue<ll>s; memset(vis,0,sizeof(vis)); for(ll i=1; i<=tt; i++)dist[i]=0x3f3f3f3f; dist[ss]=0; s.push(ss); while(!s.empty()) { ll u=s.front(); s.pop(); vis[u]=0; for(ll i=head[u];i!=-1;i=e[i].next) { ll v=e[i].to; ll w=e[i].w; if(dist[v]>dist[u]+w) { dist[v]=dist[u]+w; if(vis[v]==0) { vis[v]=1; s.push(v); } } } } ans=min(ans,dist[tt]); } void Slove() { ans=0x3f3f3f3f; ll temp=20; while(temp--) { ss=n+1; tt=n+2; cont=0; memset(head,-1,sizeof(head)); random_shuffle(ned,ned+K); for(ll i=1; i<m; i++) { add(xx[i],yy[i],ww[i]); } for(ll i=0; i<K/2; i++) { add(ss,ned[i],0); } for(ll i=K/2;i<K;i++) { add(ned[i],tt,0); } SPFA(); } printf("%I64d\n",ans); } int main() { ll kase=0; ll t; scanf("%I64d",&t); while(t--) { scanf("%I64d%I64d",&n,&m); cont=0; memset(head,-1,sizeof(head)); for(ll i=1; i<=m; i++) { scanf("%I64d%I64d%I64d",&xx[i],&yy[i],&ww[i]); } scanf("%I64d",&K); for(ll i=0; i<K; i++)scanf("%I64d",&ned[i]); printf("Case #%I64d: ",++kase); Slove(); } }
- HDU 6166 Senior Pan (思维枚举+最短路 求最近点对)
- HDU 6166 Senior Pan(思维 最短路)
- Hdu 6166 Senior Pan【思维+随机化+最短路】好题~
- hdu 6166 Senior Pan 最短路
- hdu 6166 Senior Pan(最短路)
- 2017 Multi-University Training Contest 9 && HDU 6166 Senior Pan 【最短路+思维】
- hdu6166-最短路&二进制枚举-Senior Pan
- HDU 6166 Senior Pan (最短路 好题)
- HDU 6166 Senior Pan 【二进制分组最短路 】 好题
- HDU 6166 && 2017 多校训练:Senior Pan(最短路)
- HDU 6166 Senior Pan spfa(顶点集间最短路)
- HDU 6166 Senior Pan
- HDU 6166 Senior Pan
- [HDU 6166] Senior Pan
- HDU 6166 Senior Pan
- hdu-6166 Senior Pan
- HDU 6166 Senior Pan
- HDU 6166 Senior Pan
- 1002. 写出这个数 (20)
- 【POJ】3111
- Git学习笔记(二)
- 关于Linux常用基础命令的总结
- JSP学习总结
- HDU 6166 Senior Pan (思维枚举+最短路 求最近点对)
- Python+Django实现文件的下载
- AtomicInteger研究
- wxpython和python连接oracle
- 英文学习20170825
- 算法提高 ADV-93 任意年月日历输出
- UE4蓝图初级教程之日夜交替
- fluent c++ blog
- 【Android】Animations(一)