[COCI2011-2012#7] KAMPANJA
神奇A题系列。。。。。。
本来以为这个题是个 网络流 或者其他 神奇的多项式算法的题,于是就一直没有想出来。。。。。。
然后再想想。。。边数点数同阶。都是不超过200的,,,说不定搜一搜剪剪枝就过了呢233333
因为之前有了UVA12035那个题的多达接近十种剪枝的磨练(那些剪枝加起来想了几个月。。。。),所以对于本题的剪枝就是小菜一碟了23333
第一个剪枝:首先只需要保留和1,2点同属于一个强连通分量的点(如果1和2不在同一强连通分量那么无解,但是本题好像并没有这样的数据),这个的话随便跑个tarjan就好了。
然后可以发现,如果确定了1到2的路径的话,那么再从2到1就是一个点权是0或1的最短路问题了。出现在之前1到2路径上的点的点权是0,其他的点权是1。
虽然点权不是都一样的,但是依然可以直接bfs出来,因为有0这个特殊的点权啊2333,一个双端队列就解决了。
所以这一部分就直接降成O(1所在强连通分量的大小)。
第二个剪枝:按理说我们是要搜出1到2的所有路径的,每搜到一条就bfs一遍更新答案。但是如果当前搜到一半的路径经过的点数 已经大于 之前的最小答案,直接剪掉(最优性剪枝)。
一个奇奇怪怪不知道有没有用的剪枝:按理说我们应该是优先搜1到2经过点数少的路径,然后bfs。但是这样不一定是更优的,极有可能被卡(其实是我懒得预处理dist 23333),所以在搜路径的时候,我是random_shuffle的路径后继端点。
这样做的好处是很快就能搜到一个正常的答案,不容易被数据卡,从而避免了很多不必要的搜索;坏处是搜到最后的答案是需要一定时间的(但从评测来看还好啦)
#include<bits/stdc++.h> #define ll long long using namespace std; #define pb push_back const int maxn=205; stack<int> s; int H,T,q[maxn*23]; int hd[maxn],ne[maxn],to[maxn],dc,lt[maxn]; int num,n,m,d[maxn],dfn[maxn],low[maxn],k,ans=1<<30; inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;} bool v[maxn],can[maxn]; void tarjan(int x){ dfn[x]=low[x]=++dc,s.push(x); for(int i=hd[x];i;i=ne[i]) if(!dfn[to[i]]) tarjan(to[i]),low[x]=min(low[x],low[to[i]]); else if(!lt[to[i]]) low[x]=min(low[x],dfn[to[i]]); if(low[x]==dfn[x]){ k++; for(int c=s.top();;c=s.top()){ lt[c]=k,s.pop(); if(c==x) break; } } } inline int calc(){ q[H=T=1000]=2,memset(d,-1,sizeof(d)),d[2]=0; int x; while(H<=T){ x=q[H++]; for(int i=hd[x];i;i=ne[i]) if(can[to[i]]&&d[to[i]]<0){ if(v[to[i]]) d[to[i]]=d[x],q[--H]=to[i]; else d[to[i]]=d[x]+1,q[++T]=to[i]; } } return d[1]; } void dfs(int x,int y){ if(y>=ans) return; if(x==2){ ans=min(ans,y+calc()); return;} vector<int> g; for(int i=hd[x];i;i=ne[i]) if(can[to[i]]&&!v[to[i]]) g.pb(to[i]); random_shuffle(g.begin(),g.end()); for(int i=g.size()-1,o;i>=0;i--){ o=g[i],v[o]=1; dfs(o,y+1),v[o]=0; } } int main(){ scanf("%d%d",&n,&m); int uu,vv; for(int i=1;i<=m;i++) scanf("%d%d",&uu,&vv),add(uu,vv); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); if(lt[1]!=lt[2]){ puts("-1"); return 0;} for(int i=1;i<=n;i++) if(lt[i]==lt[1]) can[i]=1; v[1]=1,dfs(1,1); cout<<ans<<endl; return 0; }