codevs 3298 幸福的烦恼

| OI, CSDN

题目描述 Description

GFS准备了一场丰厚的晚宴来宴请他的MM们,共有n个MM要来参加这场宴会,GFS为她们准备了m种菜品。但并不是每个 MM都喜欢所有的菜,每个MM都有自己喜欢吃菜品,而如果餐桌上相邻两个MM有共同喜欢的菜,她们就会互相争抢。为了维护世界的和谐,聪明的 GFS必须合理安排好他的MM们的座次。每张餐桌都是圆形的,GFS拥有足够多的桌子来安排他的 MM,但是为了防止MM们产生孤单的感觉,不允许有MM单桌的情形,也就是说每张桌子至少有两个MM。当然了,每个MM都不能与旁边的两个MM有共同喜欢吃的菜。 问是否存在这样的安排方案使得每个MM都能满意。

输入描述 Input Description

第一行一个正整数T,表示数据组数。 对于每个测试数据,第一行为两个正整数n和m。 接下来n行,每行第一个数k,表示这个MM有k个喜欢的菜品,之后k个数为MM喜欢的菜品的编号。菜品编号为1到m。

输出描述 Output Description

共包含T行,每行“Yes”或“No”。“Yes”表示存在一种方案,“No”表示不存在。

样例输入 Sample Input

2
3 4
2 2 4
1 3
1 2
5 4
2 1 3
2 2 4
1 2
1 3
1 4

样例输出 Sample Output

No
Yes

数据范围及提示 Data Size & Hint

对于30%的数据:N ≤ 16。 另有30%的数据:M ≤ 2。 对于100%的数据:1 ≤ T ≤ 10,1 ≤ N ≤ 500,1 ≤ M ≤ 30。


这题算是按照自己的思路写出来的。。。

首先,贾大神可贵地指出:任何大于等于四个人的桌子都可以拆成两个人的桌子。。这指引我们考虑两两配对。。(事实上我的这个方法在存在只能某几个三人组坐一起时应该是不对的。。)

将可以相邻的人连无向边。对于n是偶数,我们判断一下这个图的最小路径覆盖是否为边数除以二,如果是,那么我们就有使最小路径覆盖的边的端点的人两两一桌的方案了。n如果是奇数,枚举拆一个点,看剩下偶数个点是否满足刚才条件,再看最后拆的点能否和某人有边,如果有,就是yes。这样把数组开够之后,3个WA,一个T。

鞠大神搞到AC代码之后,我们通过对拍发现,单独的点要想和某两人做一桌,必须和两人都可!!而不是一人!!!在匹配时记录这个点和哪个点匹配。。再判断。。

1个WA,1个T。

我们终于发现匹配时得到的错误匹配要及时复原为0。。这样就不WA了。。。至于T的点,m=2,构成二分图,大概是可连的边太多。。所以直接判断两边的结点数一不一样。

我的这个方法好像和那个AC代码有很大偏差。。它不仅代码短,而且快,空间也少。。。。

总之我的脑残与日俱增。

label 10;
type
	edge=record y,res,pre:longint;end;
var
	a:array[0..2500000]of edge;
	last,q,level,cur:array[0..5000]of longint;
	vis,least:array[0..5000]of boolean;
	mm:array[0..1000,0..30]of boolean;
	flag:array[0..600,0..600]of boolean;
	e1,e2:array[0..3500000]of longint;
	pair:array[0..600]of longint;
	n,m,i,j,k,s,t,tt,tot,tote:longint;

function min(a,b:longint):longint;
	begin
		if a<b then exit(a);exit(b);
	end;

procedure insert(x,y,z:longint);
	begin
		inc(tot);
		a[tot].y:=y;
		a[tot].res:=z;
		a[tot].pre:=last[x];
		last[x]:=tot;
		inc(tot);
		a[tot].y:=x;
		a[tot].res:=0;
		a[tot].pre:=last[y];
		last[y]:=tot;
	end;

function bfs:boolean;
	var h,tail,x,y,k:longint;
	begin
		fillchar(vis,sizeof(vis),false);
		fillchar(level,sizeof(level),0);
		vis[s]:=true;
		level[s]:=1;
		h:=0;
		tail:=1;
		q[0]:=s;
		while h<>tail do
			begin
				x:=q[h];
				inc(h);
				k:=last[x];
				while k<>0 do
					begin
						y:=a[k].y;
						if (a[k].res<>0)and(not vis[y]) then
							begin
								level[y]:=level[x]+1;
								vis[y]:=true;
								q[tail]:=y;
								inc(tail);
							end;
						k:=a[k].pre;
					end;
			end;
			exit(vis[t]);
		end;

function dfs(x,f:longint):longint;
	var flow,k,y,ans,xx,yy:longint;
		function hehe:boolean;
		begin
			if (x>0)and(x<=n)and(y>=n+1)and(y<t) then exit(true);
			if (y>0)and(y<=n)and(x>=n+1)and(x<t) then exit(true);
			exit(false);
		end;
	begin
		ans:=0;
		if (x=t)or(f=0) then exit(f);
		//writeln(x);
		k:=cur[x];
		while k<>0 do
			begin
				y:=a[k].y;
				if level[y]=level[x]+1 then
					begin
						flow:=dfs(y,min(f,a[k].res));
						if flow<>0 then
							begin
								dec(a[k].res,flow);
								inc(a[k xor 1].res,flow);
								inc(ans,flow);
								dec(f,flow);
								yy:=y;xx:=x;
								if hehe then
									begin
										if y>x then yy:=y-n else xx:=xx-n;
										//writeln('tmpp',yy,' ',xx);
										pair[pair[xx]]:=0;
										pair[pair[yy]]:=0;
										pair[xx]:=yy;
										pair[yy]:=xx;
									end;
								if f=0 then break;
							end;
					end;
				//if hehe then writeln('hehe');
				k:=a[k].pre;
				cur[x]:=k;
			end;
		exit(ans);
	end;

function check(a,b:longint):boolean;
	var i:longint;
	begin
		for i:=1 to m do
			if mm[a][i] and mm[b][i] then exit(false);//(...=...)
		exit(true);
	end;

function remove(z:longint):boolean;
	var i,j,ans,res,nn:longint;
	begin
		//writeln(z);
		ans:=0;
		fillchar(last,sizeof(last),0);
		tot:=1;
		for i:=1 to tote do
			if (e1[i]<>z)and(e2[i]<>z) then
				begin
					insert(e1[i],e2[i]+n,1);
					insert(e2[i],e1[i]+n,1);
				end;
		s:=0;
		t:=n<<1+1;
		for i:=1 to n do insert(s,i,1);
		for i:=1 to n do insert(i+n,t,1);
		//fillchar(pair,sizeof(pair),0);
		while bfs do
			begin
				fillchar(pair,sizeof(pair),0);
				cur:=last;
				inc(ans,dfs(s,maxlongint));
			end;
		if z<>3000 then nn:=n-1 else nn:=n;
		res:=nn-ans>>1;
		if res<>nn>>1 then exit(false);
		if z=3000 then exit(true);
		for i:=1 to n do
			if flag[i,z] then
				if flag[pair[i],z] then
					begin
						//for j:=1 to n do write(pair[j],' ');
						exit(true);
					end;
		exit(false);
	end;

procedure special;
	var
		c:array[0..2]of longint;
		i,j,k:longint;
	begin
		c[1]:=0;c[2]:=0;
		for i:=1 to n do
			begin
				read(k);
				if k=2 then begin writeln('No');exit;end;
				if k=0 then continue;
				read(k);
				inc(c[k]);
			end;
		if c[1]<>c[2] then writeln('No') else writeln('Yes');
	end;
	
begin
	readln(tt);
	10:
	while tt<>0 do///tt
		begin
			dec(tt);
			readln(n,m);
			if m=2 then begin special;continue;end;
			fillchar(mm,sizeof(mm),false);
			fillchar(least,sizeof(least),false);
			tote:=0;
			fillchar(e1,sizeof(e1),0);
			fillchar(e2,sizeof(e2),0);
			fillchar(flag,sizeof(flag),false);
			for i:=1 to n do
				begin
					read(j);
					while j<>0 do
						begin
							dec(j);
							read(k);
							//if k=0 then break;
							mm[i][k]:=true;
						end;
					for k:=1 to i-1 do
						if check(i,k) then
							begin
								//writeln(i,k);
								flag[i,k]:=true;
								flag[k,i]:=true;
								inc(tote);
								e1[tote]:=i;
								e2[tote]:=k;
							end;
					readln;
				end;
			if n=3 then
				begin
					if check(1,2) and check(2,3) and check(1,3) then writeln('Yes') else writeln('No');
					continue;
				end;
			if odd(n) then
				begin
					for i:=1 to n do if remove(i) then begin writeln('Yes');goto 10;end;
					writeln('No');
				end
			else if remove(3000) then writeln('Yes') else writeln('No');
		end;
	close(input);close(output);
end.

原文地址