【POJ No. 3275】奶牛排序 Ranking the Cows
【题意】
约翰想按照奶牛的产奶能力给它们排序。
已知有N (1≤N ≤1 000)头奶牛,而且知道这些奶牛的M (1≤M≤10 000)种关系,将每种关系都表示为“X Y ”,表示奶牛X 的产奶能力大于奶牛Y 。约翰想知道自己至少还要调查多少对关系才能完成整个排序。
【输入输出】
输入:
第1行包含两个整数N 和M 。第2…M +1行,每行都包含两个整数X 和Y 。X 和Y 都在1~N 范围内,表示奶牛X 的排名高于奶牛Y。
输出:
单行输出至少还要调查多少种关系才能完成整个排序。
【样例】
【思路分析】
在输入样例中,cow2 >cow1 >cow5 ,cow2 >cow3 >cow4 ,所以cow2 的排名最高。不过,约翰需要知道排名大于cow1 及cow3 的排名第二的牛,还需要通过一个问题来确定cow4 和cow5 的顺序。之后,他需要知道如果cow1 大于cow3 ,那么cow5 是否大于cow3 。他必须问三个问题才能确定排名:“cow1 >cow3 ?cow4 >cow5 ?”“cow5 >cow3?”。
【算法设计】
① 根据输入样例,创建一个有向图。
② 根据传递性,得到的已知关系有7种,分别是:1>4、1>5、2>1、2>3、2>4、2>5、3>4。
③ 对于有n 个节点的图,两两之间的关系一共有n (n -1)/2种,5个节点共有5×4/2=10种关系,还需要知道10-7=3种关系即可。
[如何得到已知关系]
利用bitset位运算,将每个节点都用一个bitset 表示。
bitset<maxn>p[maxn]; //maxn表示位数,p[] 表示二进制数组
初始化时,p [ i ][ i ]=1,即p [i ]的第i 位为1(从右侧数第0位、1位、2位)。
输入1-5,令p [1][5]=1,则p [1]=…….100010。
输入1-4,令p [1][4]=1,则p [1]=…….110010。
输入2-1,令p [2][1]=1,则p [2]=…….000110。
输入2-3,令p [2][3]=1,则p [2]=…….001110。
输入3-4,令p [3][4]=1,则p [3]=…….011000。
判断每个数组的每一位:
if(p[i][k]){
p[i] |= p[k]; //按位或运算
}
例如,p [2][1]=1,则p [2]=p [2]|p [1]= 001110 | 110010=111110。如果2和1有关系,而1和4、5有关系,则通过或运算,可以得出2和4、5也有关系。
通过此方法,可以找到每个点和其他点的关系。用ans累计每个数组元素1的个数,因为初始化时自己到自己为1,所以ans多算了n 种关系,已知关系数应为ans-n ,用n (n -1)/2减去已知关系数即可。
for(int i = 1; i <= n ; i ++){
ans += p[i].count(); //每个数组中元素1 的个数
}
cout << n * (n - 1) / 2 - ans + n << endl;
【算法实现】
#include<iostream>
#include<bitset>
using namespace std;
const int maxn = 1005;
bitset<maxn>p[maxn];
int main(){
int n , m;
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++){
p[i][i] = 1;
}
while(m --){
int u , v;
cin >> u >> v;
p[u][v] = 1;
}
for(int k = 1; k <= n ; k++){
for(int i = 1 ; i <= n ; i ++){
if(p[i][k]){
p[i] |= p[k];
}
}
}
int ans = 0;
for(int i = 1; i <= n ; i++){
ans += p[i].count();
}
cout << n * (n - 1) / 2 - ans + n << endl;
return 0;
}