hdoj3957(dancing links)跳舞链

一、.算法参考资料:

东西太多,讲不清楚。翻译过来的论文,momodi的论文以经很不错了!就直接贴链接了。

1.dancing links(跳舞链)参考资料:http://sqybi.com/

点里面的works链接里有dlx资料的压缩包

2.momodi的论文:http://gaoyunxiang.com/wp-content/uploads/2010/02/Dancing_Links.pdf

3.2011阿里巴巴程序设计公开赛解题报告:http://www.notonlysuccess.com/?p=1062

最好是先自己把这个算法写出来,然后去对比模板,这样就能发现效率的所在


二、代码(最好不要看,仅仅是为了备份用,写得又长又不好懂!)

不过用了点小小的tricky——prepare(),结果跑到了78MS,哈哈!

  

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<string>
//freopen("data.in","r",stdin);
using namespace std;
#define MAXN 1000//
struct Node
{
	int x,y;
	int l,r,u,d;
	int s;
	Node(int ll,int rr,int uu,int dd,int xx,int yy){l=ll;r=rr;u=uu;d=dd;s=0;x=xx;y=yy;}
};
struct Table
{
	int h;//头结点
	vector<Node> v;
	//i:(i<<1,(i+n)<<1)//第i个人的行和列头结点的第一模式索引
	//bool f[N+1];//标记第i个人是否有2种模式
	//最后发现是没必要的建图时可以在图中体现这种关系
	int n;//人数
	int low,high;
	bool vis[MAXN];
	//注意x结点的特殊性
	inline void delC(int x)
	{
		for(int i=v[x].d;i!=x;i=v[i].d)
		{
			v[v[i].l].r=v[i].r;
			v[v[i].r].l=v[i].l;
		}
	}
	
	inline void linkC(int x)
	{
		for(int i=v[x].u;i!=x;i=v[i].u)
		{
			v[v[i].r].l=i;
			v[v[i].l].r=i;
		}
	}
	
	inline void delR(int x)
	{
		for(int i=v[x].r;i!=x;i=v[i].r)
		{
			v[v[i].u].d=v[i].d;
			v[v[i].d].u=v[i].u;
			v[v[i].y].s--;
		}
	}
	inline void linkR(int x)
	{
		for(int i=v[x].l;i!=x;i=v[i].l)
		{
			v[v[i].u].d=i;
			v[v[i].d].u=i;
			v[v[i].y].s++;
		}
	}
	//cover
	//1.覆盖x所在列
	//2.覆盖<x1,x2,……>xi所在列
	//3.删除另外一个模式的行(有)
	//十字链表结构还存在
	inline void cover(int x)
	{
		delC(x);
		for(int i=v[x].r;i!=x;i=v[i].r)if(!v[i].s)delC(i);
		if(v[v[x].y^1].s)delR(v[x].x^1);//存在另外一种模式
	}

	//resume
	//1.删除另外一个模式的行(有)
	//2.恢复<x1,x2,……>xi所在列
	//3.恢复x所在列
	inline void resume(int x)
	{
		if(v[v[x].y^1].s)linkR(v[x].x^1);	
		for(int i=v[x].l;i!=x;i=v[i].l)if(!v[i].s)linkC(i);
		linkC(x);
	}
	void init()
	{
		v.clear();
		//创建表头,断开结点
		h=1;
		int i;
		for(i=0;i<=(n<<1|1);i++)v.push_back(Node(i,i,i,i,i,1));//2*n+2
		for(i=(n<<1)+2;i<(n<<2|2);i++)v.push_back(Node(i,i,i,i,0,i));//4*n+2
	}
	void linkHeader(int x,int y)
	{
		v[x].y=h;
		v[x].u=v[h].u;v[x].d=h;v[v[h].u].d=x;v[h].u=x;
		v[y].l=v[h].l;v[y].r=h;v[v[h].l].r=y;v[h].l=y;
		v[h].s++;//对所有的列统计个数,包括头结点
	}
	
	void add(int x,int y)
	{
		v.push_back(Node(v[x].l,x,v[y].u,y,x,y));
		int cnt=v.size()-1;//注意cnt在push_back里先加了
		v[v[x].l].r=cnt;v[x].l=cnt;v[x].s++;//用来判断是否是行头结点
		v[v[y].u].d=cnt;v[y].u=cnt;v[y].s++;
	}
	
	void build()
	{
		for(int i=1;i<=n;i++)
		{
			int m;
			scanf("%d",&m);
			int x1=i<<1,y1=(i+n)<<1;//
			linkHeader(x1,y1);
			add(x1,y1);//!!
			
			if(m==2)
			{
				int x2=x1|1,y2=y1|1;
				linkHeader(x2,y2);
				add(x1,y2);//!!0模式打败自己的两种模式
				add(x2,y1);
				add(x2,y2);
			}
			for(int j=0;j<m;j++)
			{
				int num,obj,model;
				scanf("%d",&num);
				for(int k=0;k<num;k++)
				{
					scanf("%d%d",&obj,&model);
					y1=( (obj+1+n)<<1 )|model;
					add(x1|j,y1);//第x1个人的第j种状态打败第obj+1人的model状态
				}
			}
		}
	}
	int H()
	{
		memset(vis,0,sizeof(vis));
		int ret=0;
		for(int y=v[h].r;y!=h;y=v[y].r)
		{
			if(!vis[y])
			{
				ret++;vis[y]=true;
				for(int x=v[y].d;x!=y;x=v[x].d)
				{
					for(int z=v[x].r;z!=x;z=v[z].r)
						vis[v[z].y]=true;
				}
			}
		}
		return ret;
	}
	bool dfs(int step,int cur)
	{
		if(step+H()>cur)return false;
		if(v[h].r==h)return true;
		int ms=v[v[h].r].s,my=v[h].r;
		for(int y=v[h].r;y!=h;y=v[y].r)//选出最少的一列
		{
			if(ms>v[y].s)
			{
				ms=v[y].s;
				my=y;
			}
		}
		for(int x=v[my].d;x!=my;x=v[x].d)	//my是列头
		{
			cover(x);
			if(dfs(step+1,cur))
			{
				resume(x);
				return true;
			}
			resume(x);
		}
		return false;
	}
	bool prepare()
	{
		low=0;high=n;
		int pre=v[h].r,cur;
		for(cur=v[v[h].r].r;cur!=h;cur=v[cur].r)
		{
			bool c=( ((pre^1)!=cur)||((pre^1)==cur&&v[pre].s>2) );
			if(c&&v[cur].s==2)low++;
			pre=cur;
		}
		if(low==0)low=1;
		else high=n-1;

		int	defeat=1;//
		for(int x=v[h].d;x!=h;x=v[x].d)
		{
			int tmp=0;
			int y1=v[v[x].r].y;
			bool first=true;
			for(cur=v[v[x].r].r;cur!=x;cur=v[cur].r)
			{
				int y2=v[cur].y;
				bool c1=y2==(y1^1);
				bool c2=!c1&&v[y2^1].s==0;
				if(c1&&c2)tmp++;//
				if((y2&1)&&(!c1)&&v[y2^1].s>2&&first)
				{//两种模式,并且另一种也能被打败
					first=false;
					tmp++;
				}
				y1=y2;
			}
			if(tmp>defeat)defeat=tmp;
		}
		int must=n-defeat+1;
		if(high>must)high=must;
		if(low==high)return true;
		return false;
	}
	
	int dlx()
	{
		scanf("%d",&n);
		init();		
		build();
	//	output(v.size());
		if(prepare())return low;
//		low=1;high=n;
		while(low<high)
		{
			int mid=(low+high)>>1;
			if(dfs(0,mid))high=mid;
			else low=mid+1;
		}
		return low;
	}
	void output(int cnt)
	{
		for(int i=1;i<cnt;i++)
		{
			//printf("%d %d %d %d\n",i,v[i].x,v[i].y,v[i].s);
			printf("%d %d %d %d %d\n",i,v[i].l,v[i].r,v[i].u,v[i].d);
		}
		cout<<endl;
	}
}G;
int main()
{
//	freopen("data.in","r",stdin);
	int T;
	cin>>T;
	int cases=1;
	while(T--)
	{
		printf("Case %d: %d\n",cases++,G.dlx());
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值