词典中有限制交换的矩阵中最小的排列

时间:2013-04-04 17:36:10

标签: algorithm

Facebook 求职面试能力测试中询问了以下问题:

A permutation is a list of K numbers, each between 1 and K (both inclusive),
that has no duplicate elements.

Permutation X is lexicographically smaller than Permutation Y iff for some
i <= K:

All of the first i-1 elements of X are equal to first i-1 elements of Y.
ith element of X is smaller than ith element of Y.
You are given a permutation P, you can exchange some of its elements as many 
times as you want in any order. You have to find the lexicographically smallest     
Permutation that you can obtain from P.

K is less than 101.

Input Format:
First line of input contains K being the size of permutation.
Next line contains K single spaced numbers indicating the permutation.
Each of next K lines contains K characters, character j of line i is equal to
'Y' if you can exchange ith and jth element of a permutation, and 
'N' otherwise.

Output Format:
Print K numbers with a space separating each of them indicating the 
permutation.

Sample Input
3
3 1 2
NNY
NNN
YNN

Sample Output
2 1 3
Sample Input 
3
3 2 1
NYN
YNY
NYN

Sample Output

1 2 3 

In the first example you can exchange first element with last element to
obtain 2 1 3 from 3 1 2.

我做了什么?

我首先生成了所有排列。

然后,我放弃了那些不可行的排列。 在示例1中:1 3 3不可行,因为位置1和2是不可更改的。

从所有允许的排列列表中,我选择了字典最小的排列作为解决方案。

上述解决方案的问题

我的解决方案适用于K<=25。当K的大小大于25时,解决方案非常慢。对于K=100,我甚至没有在60 minutes中获得输出。

我的问题是:

我应该如何优化我的解决方案?

可以在不生成所有排列的情况下完成吗?

使用说明和伪代码(或代码)的更好解决方案将非常有用。

谢谢!

3 个答案:

答案 0 :(得分:4)

我的解决方案适用于K <= 25。当K的大小大于25时,解决方案非常慢。

您的解决方案将非常缓慢。当您生成所有排列时,生成排列的总体复杂性是:

O(2^K)

因此,O(2 ^ K)将需要一年,因为K可以大到100。

可以在不生成所有排列的情况下完成吗?

,可以在不生成所有排列的情况下完成。

我应该如何优化我的解决方案?

您可以使用图论中的( DFS Connected Component )概念在线性时间内解决此问题。

请注意我们将采用第二个例子来解释我将要描述的步骤(涉及算法)

第1步: 使用K-1顶点构造图形 G 。 因此V = {0,1,2}

第2步: 每当交换这两个位置的元素时,允许边 e 连接两个顶点。 因此边缘是:E = {(0,1),(1,0),(1,2),(2,1)}

第3步: 找到此图的所有连通分量(CC) G(V,E)。 在示例2中:

所有CC都是:

CC1: {0, 1, 2}

第4步: 对于每个连接的组件,对连接组件中可用的元素进行排序,使smallest index within the connected component gets smallest element,第二小索引获得第二个最小元素等等。

示例2

Smallest index in CC1 = 0
Smallest index in CC1 = 1
Smallest index in CC1 = 2
  • CC1中最小的索引0获得最小的元素。最少 元件= 1。

  • CC1中的第二个较小的索引获得第二个最小的元素。第二 最小指数= 2.

  • CC1中的第三个较小的索引获得第三个最小的元素。第三 最小指数= 3.

因此,按照上述规则对CC1进行排序后的结果为(1,2,3)。

当对所有连接的组件完成第4步时,我们的排列可能性最低。

因此,1 2 3是示例2中按字典顺序排列的最小排列。

伪代码(或代码)将非常有用。

正如我已经描述过的逻辑,这里是C ++中的代码:

vector<int>TMP_IP;
char Adjacency[LIMIT][LIMIT];
vector<vector<int> >ADJ_vector(LIMIT);
int MARKED[LIMIT];
vector<int>connected_COMPONENTS;
vector<int>Positions_vector;
void DepthFirstTraversal(int u)
{
     MARKED[u]=1;
     connected_COMPONENTS.push_back(u);
     for(int j=0;j<ADJ_vector[u].size();++j)
          if(!MARKED[ADJ_vector[u][j]] )
             DepthFirstTraversal(ADJ_vector[u][j]);
}
//Print result
void lexo_smallest(int K)
{
     for(int i=0;i<K;++i)
                  cout<<TMP_IP[i]<<" ";
             cout<<endl;
}
int main()
{
    int K,ip;
    string rows[109];
    scanf("%d",&K);
    for(int i=0;i<K;++i)
    {
            scanf("%d",&ip);
            TMP_IP.push_back(ip);
    }

    for(int i=0;i<K;++i)
               cin>>rows[i];


    for(int i=0;i<K;++i)
         for(int j=0;j<rows[i].size();++j)
             Adjacency[i][j]=rows[i][j];


    for(int i=0;i<K;++i)
    for(int j=0;j<K;++j)
     if(Adjacency[i][j]=='Y')
         ADJ_vector[i].push_back(j);  

            for( int i = 0 ; i <K ; ++i )
            {   
                if( !MARKED[ i ] )
                { 

                    DepthFirstTraversal( i ); 
                    for(int x=0;x<connected_COMPONENTS.size();++x)
                    {
                            Positions_vector.push_back(TMP_IP[connected_COMPONENTS[x]]);
                    }
                    sort(connected_COMPONENTS.begin(),connected_COMPONENTS.end());
                    sort(Positions_vector.begin(),Positions_vector.end());
                    for(int x=0;x<connected_COMPONENTS.size();++x)
                    {
                            TMP_IP[connected_COMPONENTS[x]]=Positions_vector[x];
                    }
                    connected_COMPONENTS.clear();
                    Positions_vector.clear();

                }
            }
            lexo_smallest(K);

    return 0;
}

<强> DEMO @ IDEONE

上述解决方案的复杂性:

输入的总时间是O(K ^ 2)。

上述算法的复杂度与DFS相同。 O(V + E)。

总时间:O(K ^ 2)+ O(V + E)

即使K = 5000,上述解决方案也非常快。

答案 1 :(得分:0)

在此,首先需要形成一个新矩阵,如果a(i,j)Y,那么这意味着jth位置的元素可以来自ith place

通过应用Floyd Warshall's Algorithm,通过将所有Y替换为1并将所有N替换为无穷大(或非常大的数字),可以轻松完成此任务。因此,在应用Floyd Warshall之后,如果任何元素a(i,j)小于infinity,则jth position处的元素可以放置在i位置。

现在任务很简单。贪婪地选择元素,即对于每个i,您将获得可以与之交换元素的元素列表。所以现在顺序从第1位到第1位,对于每个i,找到a(i,j)Y(即小于无穷大)的最小值(索引j)的元素并交换{ {1}}和i

答案 2 :(得分:0)

您可以逐个地点进行。

对于最左侧的位置,找到您可以找到的最小数字并将其交换到位置1.这可以通过检查从1位置到第一个位置的路径是否存在等来完成。