poj 3275 "Ranking the Cows"(DFS or Floyd+bitset<>)
题意:
农场主 FJ 有 n 头奶牛,现在给你 m 对关系(x,y)表示奶牛x的产奶速率高于奶牛y;
FJ 想按照奶牛的产奶速率由高到低排列这些奶牛,但是这 m 对关系可能不能精确确定这 n 头奶牛的关系;
问最少需要额外增加多少对关系使得可以确定这 n 头奶牛的顺序;
题解:
之所以做这道题,是因为在补CF的题时用到了bitset<>;
搜这个容器的用法是看到了一篇标题为POJ-3275:奶牛排序Ranking the Cows(Floyd、bitset)的文章;
正好拿着道题练练bitset<>;
但是一做,发现,这道题和省赛的L题好像啊,做法完全相同,只是在输出结果上处理了一下;
下午在补一下如何用bitset<>做这道题,先贴上DFS暴力AC代码;
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 const int maxn=1e3+50; 7 8 int n,m; 9 int num; 10 int head[maxn]; 11 struct Edge 12 { 13 int to; 14 int next; 15 }G[maxn*10*2]; 16 void addEdge(int u,int v) 17 { 18 G[num]={v,head[u]}; 19 head[u]=num++; 20 } 21 bool vis[maxn]; 22 23 int DFS(int u) 24 { 25 int ans=1; 26 vis[u]=true; 27 for(int i=head[u];~i;i=G[i].next) 28 { 29 int v=G[i].to; 30 if(vis[v] || (i&1)) 31 continue; 32 ans += DFS(v); 33 } 34 return ans; 35 } 36 int RDFS(int u) 37 { 38 int ans=1; 39 vis[u]=true; 40 for(int i=head[u];~i;i=G[i].next) 41 { 42 int v=G[i].to; 43 if(vis[v] || !(i&1)) 44 continue; 45 ans += RDFS(v); 46 } 47 return ans; 48 } 49 int Solve() 50 { 51 int ans=0; 52 for(int i=1;i <= n;++i) 53 { 54 mem(vis,false); 55 int t1=DFS(i); 56 mem(vis,false); 57 int t2=RDFS(i); 58 ///第i头奶牛可以确定的奶牛个数为t1+t2-1 59 ans += n-(t1+t2-1); 60 } 61 return ans>>1; 62 } 63 void Init() 64 { 65 num=0; 66 mem(head,-1); 67 } 68 int main() 69 { 70 while(~scanf("%d%d",&n,&m)) 71 { 72 Init(); 73 for(int i=1;i <= m;++i) 74 { 75 int u,v; 76 scanf("%d%d",&u,&v); 77 addEdge(u,v); 78 addEdge(v,u); 79 } 80 printf("%d\n",Solve()); 81 } 82 return 0; 83 }
思路2:(来自上述链接文章)
确定这 n 头奶牛的顺序需要 n*(n-1)/2 对关系;
(X,Y)代表 rankX > rankY
已知关系 (X,Y),(Y,Z),那么,根据传递性可得隐藏关系(X,Z);
如何根据给出的m条关系找到所有的隐藏关系呢?
Floyd传递闭包;
AC代码1:
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 using namespace std; 6 #define mem(a,b) memset(a,b,sizeof(a)) 7 const int maxn=1e3+50; 8 9 int n,m; 10 bool e[maxn][maxn]; 11 vector<int >in[maxn],out[maxn]; 12 ///in[u]:指向u的节点,out[u]:u指出去的节点 13 14 int Solve() 15 { 16 int ans=0; 17 for(int k=1;k <= n;++k) 18 { 19 for(int i=0;i < in[k].size();++i) 20 { 21 for(int j=0;j < out[k].size();++j) 22 { 23 int u=in[k][i]; 24 int v=out[k][j]; 25 if(!e[u][v])///隐藏关系u->v 26 { 27 e[u][v]=true; 28 out[u].push_back(v); 29 in[v].push_back(u); 30 ans++; 31 } 32 } 33 } 34 } 35 ///m:已知关系对 36 ///ans:隐藏关系对 37 return n*(n-1)/2-m-ans; 38 } 39 int main() 40 { 41 scanf("%d%d",&n,&m); 42 for(int i=1;i <= m;++i) 43 { 44 int u,v; 45 scanf("%d%d",&u,&v); 46 in[v].push_back(u); 47 out[u].push_back(v); 48 e[u][v]=true; 49 } 50 printf("%d\n",Solve()); 51 52 return 0; 53 }
另一种写法就是用到了bitset<>容器;
bitset<1001>_bit[1001]; 对于输入的关系<u,v>; _bit[u].set(v);//将第v为置位1,表示有一条u->v的边
如何找到所有的隐藏关系呢?
for(int i=1;i <= n;++i) for(int j=1;j <= n;++j) if(_bit[j][i]) _bit[j] |= _bit[i];///让j节点指向i节点所有指出去的边
晚上一直困惑,为什么将if()及其之后的语句改为
if(_bit[i][j]) _bit[i] |= _bit[j];
就wa了,找了许久,终于找到了;
对于如下关系:
(①,③) , (③,②) , (②,④)
(①->③->②->④)
当 i = 1 时,如果按照更改后的写法,①只会更新出<①,②>而不会更新出关系<①,④>(纸上画一下就出来了);
所以说,要更新内层循环的节点,这样更新的彻底;
AC代码2:
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 #include<bitset> 6 using namespace std; 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 const int maxn=1e3+50; 9 10 int n,m; 11 bitset<maxn>_bit[maxn]; 12 13 int Solve() 14 { 15 for(int i=1;i <= n;++i) 16 for(int j=1;j <= n;++j) 17 if(_bit[j][i]) 18 _bit[j] |= _bit[i];///让j节点指向i节点所有指出去的边 19 20 int ans=0; 21 for(int i=1;i <= n;++i) 22 ans += _bit[i].count(); 23 24 ///ans:m对已有关系对+隐藏关系对 25 return n*(n-1)/2-ans; 26 } 27 int main() 28 { 29 scanf("%d%d",&n,&m); 30 for(int i=1;i <= m;++i) 31 { 32 int u,v; 33 scanf("%d%d",&u,&v); 34 _bit[u].set(v); 35 } 36 printf("%d\n",Solve()); 37 38 return 0; 39 }