ural1781 Clean Code

ural1781 Clean Code

我们记一次操作为一个二元组(i,j)

考虑矩阵中的元素a,它原来的位置是(x,y),则经过操作(i,j)后等价于分别对x和y做一次置换映射,这个置换映射就是s0=(i j),是一个轮换

因此每一个操作都可以看做一个轮换,所以操作并起来就是这些轮换的乘积,记为s,它还是一个置换映射
如果a原来的位置是(x,y),经过这一系列操作,最终的位置是(s(x),s(y))

对于每一个非零元素a(x,y),我们只要要求s(x)<=s(y)即可,当x=y时,显然有s(x)=s(y),而当x!=y时,有s(x)!=s(y)

因此只需使在x!=y时,s(x)<s(y)即可

我们可以建立一个拓扑排序模型,将1,2,...,n做拓扑排序,记它们在拓扑序列中的位置为s(1),s(2),...,s(n).

对于每一个非零元素a(x,y),且x!=y,必须满足s(x)<s(y),即从y到x添一条有向边

如果不能拓扑排序,则无解。否则得到s,把它拆成轮换的乘积,然后再把轮换拆成对换的乘积即可


#include  < iostream >
#include 
< cstdlib >
#include 
< cstdio >

using   namespace  std;

bool  d[ 120 ][ 120 ];
int  n,m, in [ 120 ],s[ 120 ],x[ 120 ],y[ 120 ];
bool  b[ 120 ];

int  main( void )
{
  
int  i,j,k,t;
  scanf(
" %d " , & n);
  
for (i = 1 ;i <= n;i ++ )
  {
    
in [i] = 0 ;
    b[i]
= true ;
  }
  
for (i = 1 ;i <= n;i ++ )
    
for (j = 1 ;j <= n;j ++ )
    {
      d[i][j]
= false ;
      scanf(
" %d " , & t);
      
if  (i != j && t != 0 )
      {
        d[i][j]
= true ;
        
in [j] ++ ;
      }
    }
  
for (i = 1 ;i <= n;i ++ )
  {
    k
= 0 ;
    
for (j = 1 ;j <= n;j ++ )
      
if  ( in [j] == 0 )
      {
        k
= j;
        
break ;
      }
    
if  (k == 0 break ;
    s[k]
= i;
    
in [k] -- ;
    
for (j = 1 ;j <= n;j ++ )
      
if  (d[k][j])
        
in [j] -- ;
  }
  
if  (i <= n)
  {
    printf(
" %d\n " , - 1 );
    
return   0 ;
  }
  m
= 0 ;
  
for (i = 1 ;i <= n;i ++ )
  {
    
if  ( ! b[i])  continue ;
    b[i]
= false ;
    
if  (s[i] == i)  continue ;
    
for (j = s[i];j != i;j = s[j])
    {
      b[j]
= false ;
      m
++ ;
      x[m]
= i;
      y[m]
= j;
    }
  }
  printf(
" %d\n " ,m);
  
for (i = 1 ;i <= m;i ++ )
    printf(
" %d %d\n " ,x[i],y[i]);
  
return   0 ;
}

你可能感兴趣的:(ural1781 Clean Code)