[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; 
}

  

posted @ 2018-05-18 12:07  蒟蒻JHY  阅读(390)  评论(0编辑  收藏  举报