zoukankan      html  css  js  c++  java
  • 「COCI 2009」POSLOZI

    • 前言

      原题链接 link

      这题数据有那么点水(?)。


    • 题目大意

      给一个长度为 (N) 的排列 ((1le Nle 12)) 。有 (M) 种允许的修改方式 ((1le Mle frac{N imes (N-1)}{2})),保证修改方式不重复,每种方式用 (L,R) 来表示,意为你可以将下标为 (L) 的数与下标为 (R) 的数交换。

      你可以修改该排列若干次,请给出一种修改方案,使原排列变为 (1,2,3,ldots,N) 。如果有多种方案,输出修改次数最少的方案。如果还有多种方案,输出任意一组即可。


    • 分析

      直接 IDA* 会被一些精心构造的数据卡掉,虽然这题没有,我们尝试换一种搜索方式。

      对交换方式建图,发现每次操作相当于交换两个相邻的点。

      考虑贪心,若一个点 (i) 已经到达了他该在的位置,我们贪心的希望他不在移动,把他标记住。

      固定了第 (i) 个点,就意味着其他点移动时候不能经过这个点。但这样可能会导致结果无解或者结果不是最优的情况。

      这意味着固定第 (i) 个点前,一定要先固定另外若干个点。

      所以我们考虑暴力枚举每个点固定的顺序,按照这个顺序让每个点到他应该到的地方即可。这里显然跑最短路就行。

      这样上限是 (12!) 的,但我们可以一边跑一边维护是否有解的情况,可以大力剪枝。

      然后加个 IDA* 就跑的飞快了。

      需要注意的是,对于每个点移动过程中,可能会出现多个最短路,此时每次优先跑第一个最短路即可。

      简单证明一下,对于路径 (x)(y) ,若有多条最短路,优先跑第一条路径。但一定存在一个排列顺序,使在第一条路径上存在一个点被提前标记,这样第一条路径就不能走了。按照之前优先程度,那个排列此时会跑第二个最短路。

      第三个最短路或以上同理。

      但这个做法常数有点大,在这里需要开 O2 才能通过。


    • 代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=15*15*15,M=15*15*15;
    const int inf=1e9;
    struct node{
    	int x,y;
    }b[M];
    struct edge{
    	int v,nx;
    	int id;
    }e[M];
    int n,m,ne,f[N],a[N],p[N],d[N],lst[N],nxt[N],iid[N];
    int ansn,tot,ans[M],t[M],dis[16][16];
    bool vis[N];
    queue<int> q;
    int solve(int s,int t)
    {	
    	for(int i=1;i<=n;i++)d[i]=inf,lst[i]=0;
    	while(!q.empty())q.pop();
    	d[s]=0;q.push(s);
    	while(!q.empty())
    	{	
    		int u=q.front();
    		q.pop();
    		for(int i=f[u];i;i=e[i].nx)
    		{	
    			int v=e[i].v;
    			if(d[v]!=inf)continue;
    			if(vis[v])continue;
    			d[v]=d[u]+1;
    			lst[v]=u;
    			iid[v]=e[i].id;
    			q.push(v);
    		}
    	}
    	return d[t];
    }
    void getans(int res)
    {	
    	if(res>=ansn)return;
    	ansn=res;
    	for(int i=1;i<=res;i++)
    		ans[i]=t[i];
    }
    int geth()
    {	
    	int res=0;
    	for(int i=1;i<=n;i++)
    		res+=dis[i][p[i]];
    	return res;
    }
    void dfs(int x,int res,int deep)
    {	
    	if(x==n+1){getans(res);return;}
    	int h=geth();
    	if(res>=deep)
    	{	
    		if(h==0){getans(res);return;}
    		return;
    	}
    	if(h/2+res>=ansn)return;
    	if(res>=ansn)return;
    	for(int i=1;i<=n;i++)
    	{	
    		if(vis[i])continue;
    		int cnt=solve(i,p[i]);
    		if(cnt==inf)continue;
    		vis[i]=1;
    		for(int u=p[i];u!=i&&lst[u]!=0;u=lst[u])
    		{	
    			swap(p[a[u]],p[a[lst[u]]]);swap(a[u],a[lst[u]]);
    			t[++tot]=iid[u];
    		}
    		dfs(x+1,tot,deep);
    		vis[i]=0;
    		solve(a[i],i);
    		while(tot>res)
    		{	
    			swap(p[a[b[t[tot]].x]],p[a[b[t[tot]].y]]);
    			swap(a[b[t[tot]].x],a[b[t[tot]].y]);
    			tot--;
    		}
    	}
    }
    void read(int u,int v,int w)
    {	
    	e[++ne].v=v;
    	e[ne].nx=f[u];
    	f[u]=ne;
    	e[ne].id=w;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{	
    		scanf("%d",&a[i]);
    		p[a[i]]=i;
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			if(i!=j)dis[i][j]=inf;
    	for(int i=1;i<=m;i++)
    	{	
    		scanf("%d%d",&b[i].x,&b[i].y);
    		if(b[i].x==b[i].y)continue;
    		read(b[i].x,b[i].y,i);
    		read(b[i].y,b[i].x,i);
    		dis[b[i].x][b[i].y]=dis[b[i].y][b[i].x]=1;
    	}
    	for(int k=1;k<=n;k++)
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    	int deep=0;
    	while(deep<=n*n)
    	{	
    		ansn=inf;
    		dfs(1,0,deep);
    		if(ansn!=inf)break;
    		deep++;
    	}
    	printf("%d
    ",ansn);
    	for(int i=1;i<=ansn;i++)
    		printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Python爬虫 -- 抓取电影天堂8分以上电影
    Kotlin & Vertx 构建web服务
    js promise 风格编程
    Java Config 下的Spring Test方式
    (转)SpringSecurity扩展User类,获取Session
    maven 打包 xml文件
    EL表达式的操作符
    mysql 去除空格
    spring security
    GoogleApis 屏蔽
  • 原文地址:https://www.cnblogs.com/Rainy7/p/solution-COCI-2009-POSLOZI.html
Copyright © 2011-2022 走看看