来源:洛谷 编号:P1078 地址:https://www.luogu.org/problemnew/show/P1078
题目描述
NOIP普及组 2012 T4
比较复杂的最短路题目
解题思路
听说这道题是错题,数据水得不要不要的?那我可要拿出骗分大法了!
暴力搜索!
首先,可以明确,由于使者太懒,任何国家都可以被视为排斥相同文化的外来人。
这道题刚开始看起来像是最短路(也确实是)。最短路的算法大家经常谈论的都涉及到点上标签的迭代,但是最短路实际上有种贪心做法,就是 UCS(Uniform cost search, 亦称最短路Label Setting),思想同BFS,只是使用优先队列,总是将离起点最近的节点排在队列的前面,这样,总是可以保证前沿的节点到起点的距离相对一致。
但是Label Setting充其量就是暴力搜索。我们用结构体_entry
来存储搜索状态,包含到起点的距离、当前所在国家和一个 STL set——已学习的文化(这当然是最暴力的做法)。每当扩展一个在优先队列中的节点时,要检查其相邻节点的文化与已学习文化的set是否相容。
初始状态,距离为0,当前位置为起点,已学习的文化只有一个——起点的文化。
当搜索算法找到终点时,返回距离即可。如果队列空了还没找到,显然就返回-1.
以下是坑点(很重要)
36分——小于号重载错误
第一次提交36分纯属忘记了优先队列会将大的元素排在前面,因此将_entry
的小于号直接定义为离起点距离的小于号(应该改为大于等于,这样距离小的entry
会排在前面)。
44分——在找到终点时就结束搜索
Label Setting不能在找到终点时就结束搜索。想象有两个节点P, Q,搜索后距离起点的距离分别为33, 34,两个节点均为下一步就能到达终点,P到达终点长度为10,Q为1.
根据Label Setting的规则,P在Q之前被展开,因此找到终点,返回最短路为43。但是显然从Q到终点的总距离只有35。显然错了。
正确的做法应该是在终点即将离开优先队列时返回最短路。
92分——被10号数据卡到
事实证明其他数据都很水,只有10号数据无解而且能诱导搜索算法运行很长一段时间。
方法:骗分。当出队操作执行超过800次时强制结束搜索,返回-1.
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll r[101][101]; //文化的排斥关系 r[i][j] struct _country{ ll r; //文化 vector<pair<ll,ll> > n; //通往其他国家的路线 }c[101]; ll n,m,k,s,t; //如题 struct _entry{ ll c; //国家 ll l; //路径长度 set<ll> study; //已学习 _entry(){ c=-1;l=0; } _entry(ll _c,ll _l,ll _i){ c=_c;l=_l; study.insert(_i); } bool operator<(const _entry &other) const { return l>=other.l; } }; priority_queue<_entry> que; ll checkIfError(set<ll> studies,ll c) { //检查已学习数组是否与新文化冲突 for(ll i=1;i<=k;i++) { if(r[c][i] && studies.count(i)) return true; } return false; } ll search() { ll elasped=0; que.push(_entry(s,0,c[s].r)); //初始 while(!que.empty()) { _country curr=c[que.top().c]; //出发点国家节点 _entry ent=que.top(); //出发点的Entry信息 elasped++; if(elasped>800) return -1; if(ent.c==t) { return ent.l; } que.pop(); for(ll i=0;i<curr.n.size();i++) { _entry tmp=ent; tmp.l+=curr.n[i].second; tmp.c=curr.n[i].first; //cout<<"From "<<ent.c<<" to "<<tmp.c<<endl; if(checkIfError(ent.study,c[tmp.c].r)) continue; //判断冲突 tmp.study.insert(c[tmp.c].r); que.push(tmp); } } return -1; } int main() { cin>>n>>k>>m>>s>>t; for(ll i=1;i<=n;i++) { cin>>c[i].r; //输入文化 } for(ll i=1;i<=k;i++) { for(ll j=1;j<=k;j++) { cin>>r[i][j]; if(i==j) r[i][j]=1; //不能学习多次,即所有文化排斥自己 } } for(ll i=1;i<=m;i++) { ll x,y,z; cin>>x>>y>>z; c[x].n.push_back(make_pair(y,z)); //输入路径 c[y].n.push_back(make_pair(x,z)); } cout<<search()<<endl;; }
Comments | NOTHING