BZOJ1077: [SCOI2008]天平

Description

  你有n个砝码,均为1克,2克或者3克。你并不清楚每个砝码的重量,但你知道其中一些砝码重量的大小关系。
你把其中两个砝码A和B放在天平的左边,需要另外选出两个砝码放在天平的右边。问:有多少种选法使得天平的左
边重(c1)、一样重(c2)、右边重(c3)?(只有结果保证惟一的选法才统计在内)

Input

  第一行包含三个正整数n,A,B(1<=A,B<=N,A和B不相等)。砝码编号为1~N。以下n行包含重量关系矩阵,
其中第i行第j个字符为加号“+”表示砝码i比砝码j重,减号“-”表示砝码i比砝码j轻,等号“=”表示砝码i和砝
码j一样重,问号“?”表示二者的关系未知。存在一种情况符合该矩阵

Output

  仅一行,包含三个整数,即c1,c2和c3。

Sample Input

6 2 5
?+????
-?+???
?-????
????+?
???-?+
????-?

Sample Output

1 4 1

HINT

【数据规模】 4<=n<=50

Source

思路题
把相等的用并查集搞一搞
其他关系,如果一个东西有+有-,那么它是2,并可以更新其他东西
然后把每个的[l,r]搞出来
枚举,check对应关系
看了题解才知道应该用sgn()判,不然3!=1+1会挂
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 52;

int fa[MAXN], l[MAXN], r[MAXN], g[MAXN][MAXN], v[MAXN];
int ans1, ans2, ans3;
int n, A, B;
int q[MAXN], top;
char ch[MAXN][MAXN];

inline int findfa(int x) { while( x ^ fa[ x ] ) x = fa[ x ] = fa[ fa[ x ] ]; return x; }

inline void uni(int x, int y) { fa[ findfa( x ) ] = findfa( y ); }

inline int sgn(int x) { return !x ? 0 : ( x > 0 ? 1 : -1 ); }

int main()
{
	scanf( "%d%d%d", &n, &A, &B );
	for( int i = 1 ; i <= n ; i++ ) fa[ i ] = i;
	for( int i = 1 ; i <= n ; i++ )
	{
		scanf( "%s", ch[ i ] + 1 );
		for( int j = 1 ; j <= n ; j++ ) if( ch[ i ][ j ] == '=' ) uni( i, j );
	}
	for( int i = 1 ; i <= n ; i++ )
		for( int j = 1 ; j <= n ; j++ )
			if( ch[ i ][ j ] == '+' ) g[ findfa( i ) ][ findfa( j ) ] = 1;
			else if( ch[ i ][ j ] == '-' ) g[ findfa( i ) ][ findfa( j ) ] = -1;
	for( int i = 1 ; i <= n ; i++ )
		if( findfa( i ) == i ) q[ ++top ] = i;
	for( int i = 1 ; i <= top ; i++ )
	{
		bool L = 0, R = 0;
		for( int j = 1 ; j <= top ; j++ ) L |= ( g[ q[ i ] ][ q[ j ] ] == 1 ), R |= ( g[ q[ i ] ][ q[ j ] ] == -1 );
		if( !L || !R ) continue;
		v[ q[ i ] ] = 2;
		for( int j = 1 ; j <= top ; j++ )
			if( g[ q[ i ] ][ q[ j ] ] == 1 ) v[ q[ j ] ] = 1;
			else if( g[ q[ i ] ][ q[ j ] ] == -1 ) v[ q[ j ] ] = 3;
	}
	for( int i = 1 ; i <= top ; i++ )
	{
		l[ q[ i ] ] = 1, r[ q[ i ] ] = 3;
		if( v[ q[ i ] ] ) l[ q[ i ] ] = r[ q[ i ] ] = v[ q[ i ] ];
		else for( int j = 1 ; j <= top ; j++ )
			if( g[ q[ i ] ][ q[ j ] ] == 1 ) l[ q[ i ] ] = 2;
			else if( g[ q[ i ] ][ q[ j ] ] == -1 ) r[ q[ i ] ] = 2;
	}
	for( int i = 1 ; i < n ; i++ )	if( i != A && i != B )
		for( int j = i + 1 ; j <= n ; j++ ) if( j != A && j != B )
		{
			int fi = findfa( i ), fj = findfa( j ), fa = findfa( A ), fb = findfa( B );
			int t1 = 0, t2 = 0, t3 = 0;
			for( int vi = l[ fi ] ; vi <= r[ fi ] ; vi++ )
			for( int vj = l[ fj ] ; vj <= r[ fj ] ; vj++ )
			for( int va = l[ fa ] ; va <= r[ fa ] ; va++ )
			for( int vb = l[ fb ] ; vb <= r[ fb ] ; vb++ )
			{
				int F[ 4 ] = { fi, fj, fa, fb };
				int V[ 4 ] = { vi, vj, va, vb };
				bool flag = true;
				for( int x = 0 ; x < 4 ; x++ )
					for( int y = 0 ; y < 4 ; y++ )
						if( F[ x ] == F[ y ] && V[ x ] != V[ y ] ) { flag = false; break; }
				for( int x = 0 ; x < 4 ; x++ )
					for( int y = 0 ; y < 4 ; y++ )
						if( g[ F[ x ] ][ F[ y ] ] && sgn( V[ x ] - V[ y ] ) != g[ F[ x ] ][ F[ y ] ] ) { flag = false; break; }
				if( !flag ) continue;
				if( va + vb > vi + vj ) t1 = 1;
				if( va + vb == vi + vj ) t2 = 1;
				if( va + vb < vi + vj ) t3 = 1;
			}
			if( t1 + t2 + t3 == 1 ) ans1 += t1, ans2 += t2, ans3 += t3;
		}
	cout << ans1 << ' ' << ans2 << ' ' << ans3 << endl;
}


已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页