UVALive - 7818-MicroRNA Ranking

题目链接:UVALive-7818-MicroRNA Ranking

(一)题面:

Problem description
Ahlaam is a computer science student, doing her master thesis on a bioinformatics project about MicroRNAs, special molecule types found in cells. During her thesis, she wants to find microRNAs relevant to a specific health factor in human beings.

Ahlaam has designed k microRNA ranking algorithms, each of which ranks microRNAs from a specific point of view. There are n microRNAs numbered 1 through n, and each algorithm produces one permutation of these n microRNAs. In the permutation produced by each algorithm, the first microRNA is inferred by the algorithm as the most relevant one to the health factor, and the last microRNA is inferred as the least relevant one.

Ahlaam wants to report a consensus ranking on microRNAs. In a consensus ranking, if microRNA i is ranked before another mircroRNA j, then at least half of the algorithms should have ranked i before j. Write a program to help Ahlaam find a consensus ranking. 

 
Input
There are multiple test cases in the input. The first line of each test contains two space-separated integers n (1 ≤ n ≤ 1000) and k (1 ≤ k ≤ 200), the number of microRNAs and the number of ranking algorithms, respectively. Then, there are k lines, where the i-th line contains a permutation of n numbers 1....n, representing the output of the i-th ranking algorithm. The input terminates with a line containing 0 0 which should not be processed. 

 
Output
For each test case, print a single line containing a permutation of n numbers 1; ...n, representing a possible consensus ranking. If there are more than one correct consensus rankings, print the first one in lexicographic order (a sequence a1; ...; an is lexicographically less than a sequence b1; ...; bn iff there exists a positive integer j such that ai = bi for all 1 ≤ i ≤ j - 1 and aj < bj ) . If no such a ranking exists, write “No solution” instead. 

 
Sample Input
5 3
3 2 4 1 5
4 1 5 2 3
2 4 5 1 3
5 2
5 4 3 2 1
1 2 3 4 5
4 3
1 4 2 3
4 2 3 1
3 1 2 4
0 0
Sample Output
2 4 1 5 3
1 2 3 4 5
No solution

 

(二)题意:

首先给出K个1~N排列,要求你由这K个排列构造出一个另一个1~N的排列,构造的规则是:对于数字x和y,如果在给出的K个排列中,<x出现在y前面的排列数>比<y出现在x前面的排列数>多,则在构造得到的排列中,x应该出现在y前面,反之则反之。若二者出现的次数在K个排列中出现的次数相同,则对于x、 y在构造排列中出现的先后顺序无要求。若能构造出多个满足条件的序列,输出字典序最小的那个。如果没有满足要求的序列则输出"No solution"。

 

(三)题解:

对于数字x和y,由给出的K个排列,我们可以轻松地得到x和y在构造的排列中出现的先后顺序:记录一下在K个排列中<x出现在y前面的次数>以及<y出现在x前面的次数>,比较一下即可。比如第一个样例中的数字1和2,在第一个和第三个排列中,2出现在1的前面,第二个排列中1出现在2的前面,故得到排列中2需要出现在1的前面。

依次类推,我们也就能够得到排列中任意两个数字出现的先后顺序需要满足的条件,这就相当于给了你n个任务,有些任务必须在另外一些任务之前完成,要你给出执行这些任务的顺序的一个方案。

这么一来就是一个拓扑排序的问题了(那么什么是拓扑排序呢?度娘度娘^_^)。将每一个数字看成顶点编号,那么我们由K个排列得到的任意两个数字间的需要满足的先后关系就可以建成一个有向图。比如数字x需要出现在y之前,那么就从x向y连一条有向边,若两个数字间没有先后关系则不需要连边。将所有的关系连成边以后跑一遍拓扑排序就行了。

注意一点的是这里要输出字典序最小的排列,用DFS进行拓扑排序处理字典序就不是很方便(可能是我写法问题),故在这里用的BFS进行拓扑排序,并且每次需要将当且队列中节点编号最小的节点出队,优先队列维护一下。

而对于没有解的情况就是类似于<数字x需要出现在y的前面,y要出现在z的前面,z要出现在x的前面>的情况,也就是建成的图中出现环的情况。此时拓扑排序结束时一定无法得到n个点(因为有环就必定存在某些节点的入度不为0,就不可能入队列),最后判断一下即可。

 

(四)代码:


#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#define ll long long
#define FOR(i,a,b) for(int i=a;i<b;i++)
const int maxn = 1010;
using namespace std;
int G[maxn][maxn],deg[maxn],a[maxn];
int topo[maxn],edgehead[maxn],e=1,t,n,k;
struct Edge{int from,to,next;}edges[maxn*maxn];
void Addedge(int u,int v){
    edges[e].to=v;
    edges[e].next=edgehead[u];
    edgehead[u]=e++;
}
bool Toposort(){
    int u;t=0;
    priority_queue<int,vector<int>,greater<int> >Q;
    FOR(i,0,n)if(!deg[i]){Q.push(i);}
    while(!Q.empty()){
        topo[t++]=u=Q.top();Q.pop();
        for(int i=edgehead[u];i;i=edges[i].next){
            int v=edges[i].to;deg[v]--;
            if(!deg[v])Q.push(v);
        }
    }
    return t==n;
}
inline bool in(int &x){
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+(ch&15),ch=getchar();
    return 1;
}
int main(){
//    freopen("in.txt","r",stdin);
    while(in(n)&&in(k)&&n){
        FOR(p,0,k)FOR(i,0,n){
            in(a[i]);a[i]--;
            for(int j=i-1;j>=0;j--){
                G[a[j]][a[i]]++;
            }
        }
        FOR(i,0,n)FOR(j,i+1,n){
            if(G[i][j]>G[j][i])Addedge(i,j),deg[j]++;
            if(G[i][j]<G[j][i])Addedge(j,i),deg[i]++;
        }
        if(!Toposort())printf("No solution\n");
        else {
            FOR(i,0,n-1)printf("%d ",topo[i]+1);
            printf("%d\n",topo[n-1]+1);
        }
        memset(G,0,sizeof G);
        memset(deg,0,sizeof deg);
        for(int i=0;i<n;i++)edgehead[i]=0;e=1;
    }
    return 0;
}

 

(五)总结:

怀着TLE的心态,得到AC的结果

这个题复杂度感人,O(kn^2)竟然也能过,暴力出奇迹...

话说在校内OJ过了,而开始在UVA上T了有点意外,将vector改成数组存边才过,然而在校内OJ上似乎没什么卵用

为什么他们代码可以跑得那么快...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值