您的位置:首页 > 产品设计 > UI/UE

UVALive--4255 Guess

2018-03-09 08:22 465 查看
这道题,蓝书上说的是基础题,哎,但是对于我这样的菜鸟,叫我想一天也想不出来啊。

Given a sequence of integers, a1,a2,...,an, we define its sign matrix S such that, for 1 ≤ i ≤ j ≤ n, Sij = “ + ” if ai + ... + aj > 0; Sij = “−” if ai + ... + aj < 0; and Sij = “0” otherwise. For example, if (a1,a2,a3,a4) = (−1,5,−4,2), then its sign matrix
S is a 4×4 matrix: 1 2 3 4 1 − + 0 + 2 + + + 3 − − 4 + We say that the sequence (−1,5,−4,2) generates the sign matrix. A sign matrix is valid if it can be generated by a sequence of integers. Given a sequence of integers, it is easy to compute its sign matrix.
This problem is about the opposite direction: Given a valid sign matrix, find a sequence of integers that generates the sign matrix. Note that two or more different sequences of integers can generate the same sign matrix. For example, the sequence (−2,5,−3,1)
generates the same sign matrix as the sequence (−1,5,−4,2). Write a program that, given a valid sign matrix, can find a sequence of integers that generates the sign matrix. You may assume that every integer in a sequence is between −10 and 10, both inclusive.

Input

The input consists of T test cases. The number of test cases T is given in the first line of the input. Each test case consists of two lines. The first line contains an integer n (1 ≤ n ≤ 10), where n is the length of a sequence of integers. The second line contains
a string of n(n + 1)/2 characters such that the first n characters correspond to the first row of the sign matrix, the next n−1 characters to the second row, ..., and the last character to the n-th row.

Output

For each test case, output exactly one line containing a sequence of n integers which generates the sign matrix. If more than one sequence generates the sign matrix, you may output any one of them. Every integer in the sequence must be between −10 and 10, both
inclusive.

Sample Input

3 4 -+0++++--+ 2 +++ 5 ++0+-+-+--+-+-

Sample Output

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

题目属于图论,但是要是不跟你说是图论,鬼才知道要用图论做,而且,完全不知道怎么转换成图论,

,那么,我今天就要仔细地讲一下,网上的版本讲解都太少。

首先,要做对这道题,必须要了解相关知识:①会利用前缀和求某一段区间的和,(用Sn表示1-n项的和,那么Sa-Sb 就可以表示从b+1项到a项的和)②会用拓扑排序。

拓扑排序其实挺简单的,但是,感觉自己没怎么用过。(如果不知道这两点的同学还是要补充补充下知识啊)。

首先,我们要到1-n这样一个序列,常规想法就是把1-n中每个下标当成一个点,但是,这样根本下不了手啊,如果我们把前缀和视为一个点,是不是好一点,因为,对于给的任意一个符号(+、-、0),都可以用两个前缀和相减表示,如果是+,则表示Sa-Sb>0,- 则是Sa-Sb<0,那么我们想一下,如果我们知道了所有前缀和之间的关系,我们是不是就能列出一种符合情况的前缀和数组,然后求出我们的序列。

为了更深刻的理解这句话,我们把样例操作一遍。

样例:n = 4   -+0+

                   +++

                      +-

                       +

a1,a2,a3,a4分别为这四个数

四个前缀和分别为A1,A2,A3,A4.从样例的第一行  ,我们得出     

 a1<0      a1+a2>0
a1+a2+a3=0      a1+a2+a3+a4=0

这只是最浅显的表示,实际上应该用前缀和表示更方便,如下

第一行  A1>0      A2<0
A3=0                A4>0

第二行
A2-A1>0      A3-A1>0          A4-A1>0

第三行                                      
A3-A2>0 A4-A2<0

第四行                                                                
A4-A3>0

大家不会的,一定要自己算一下啊,这一步很关键!

不过,列到这,自己硬推也能推出来了,不过还是按照算法走一遍吧。

其实第一行有更好的表示,   A1-A0>0 A2-A0<0   A3=A0    
A4-A0>0

其中A0就是0,这样就和后面几行一致了,这样的好处就是,写代码好写一些。

根据上面这些关系,如果我们能给A1、A2、A3、A4排出序来(根据它们的大小关系),是不是就能写出一组符合题意的数列来了,建议大家自己写一个出来,然后根据前缀推出原序列,最后再进行验证。

当然,给这些A1、A2、A3、A4排序,就要用到我们的拓扑排序了,那么怎么用呢?  下面讲解

A2-A1>0
就表示A2在A1的右边(图里面),所以这时候我们加一条有向边,从A1指向A2,那么进行排序的时候,A1就在前,A2就在后了,所以,转换为图轮就在这一瞬间。即每个大于号或者小于号可以建一条边,等于号不用建。根据建的图,找出拓扑序列,再给拓扑序列赋值,最后求结果。

所以,这个题目就关键的几步,①找出前缀和之间的关系   ②用拓扑排序将前缀和进行排序③给排好序的序列赋值。

还有,关于转换为图,我再提示一下吧,将0-n,每一个当做一个节点,代表前缀和的下标,那么,给一个+号,从一个点到另一个点建一条边(用领接矩阵建),具体是哪两个点,可以在图中找一下规律,发现用两个for循环可以很好表示两个节点(如果不懂,还是看一下代码吧,其实就是遍历矩阵中的一个上三角形),并且,该点的入度+1(后面拓扑排序要用到)。

还有一点,如果两个点相等,则不用建边,不过要用并查集把它们连起来,(这一步应该有其他的方法,所以大家只要考虑到相等的情况就可以了)

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
int m[12][12];
int in[12];
int n;
int rela[12];
int find( int x )
{
if( x != rela[x] )
return find(rela[x]);
return x;
}
void topsort()
{
vector<int> v;
queue<int> q;
for( int i = 0 ; i <= n ; i++ )
if( in[i] == 0 )
{
q.push(i);
v.push_back(i);
}
while( !q.empty() )
{
int t = q.front(); q.pop();
for( int i = 0 ; i <= n ; i++ )
{
if( m[t][i] == 1 )
{
m[t][i] = 0;
in[i]--;
if( in[i] == 0 )
{
q.push(i);
v.push_back(i);
}
}
}
} /* 拓扑排序完成 */
int flag;
int a[12]; //储存前缀和
memset(a,0,sizeof a);
vector<int> ans[12]; //将拓扑序列赋值的辅助二维数组
int cnt = 0;
ans[0].push_back(v[0]);
flag = v[0];
int pos = 0;
for( int i = 1 ; i < v.size() ; i++ )
{
if( find(v[i]) != find(flag) ) //如果两个数不在一个集合,说明两个前缀不相等
{
flag = v[i];
cnt++;
ans[cnt].push_back(v[i]);
}
else ans[cnt].push_back(v[i]); //在一个集合,两个前缀相等
if( v[i] == 0 )
pos = cnt;
}
int m = 0; //a0的前缀是0
for( int i = pos ; i >= 0 ; i-- ) //从中间开始向左,给前缀依次赋值
{
for( int j = 0 ; j < ans[i].size() ; j++ )
a[ ans[i][j] ] = m;
m--; //依次减1
}
m = 1;
for( int i = pos+1 ; i <= cnt ; i++ )
{
for( int j = 0 ; j < ans[i].size() ; j++ )
a[ ans[i][j] ] = m;
m++; //向右依次加1
}
for( int i = 1 ; i <= n ; i++ )
{
printf("%d",a[i]-a[i-1]); //由前缀和输出序列
if( i == n )
printf("\n");
else printf(" ");
}
}

void merge( int a,int b )
{
int x = find(a);
int y = find(b);
if( x != y )
{
rela[x] = y;
}
}
int main()
{
int T;
scanf("%d",&T);
while( T-- )
{
scanf("%d",&n);
getchar();
for( int i = 0 ; i <= n; i++ )
rela[i] = i;
memset(m,0,sizeof m);
memset(in,0,sizeof in);
char ch;
for( int i = 0 ; i < n ; i++ )
for( int j = i+1 ; j <= n ; j++ )
{
ch = getchar();
if( ch == '+' )
{
m[i][j] = 1;
in[j]++;
}
else if( ch == '-' )
{
m[j][i] = 1;
in[i]++;
}
else merge(i,j);
}
topsort();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM 图论 拓扑排序