COJ1081(集训队分组)
Description
中南大学ACM的暑期集训马上就要开始了,这次集训会将全体N名集训队员(编号分别为1, 2, …, N)按集训选拔赛的排名分成两组,前K名队员分入A组,其余队员分入B组。
但现在助理教练CSGrandeur一不小心把集训选拔赛的排名弄丢了,而之前又没将A组和B组的人员确定出来,于是CSGrandeur打算问一下集训人员他们的名次各是怎样的,以此来确定一下A组的队员。
然而集训队员们都视名次如粪土,只是隐约记得某些人排在了自己的后面,最终反馈到CSGrandeur这里的一共有M条信息,每条信息都可以用一个二元组(x, y) (x!=y)表示,含义为第x名队员记得第y名队员的排名比自己的要靠后。
现在CSGrandeur想知道,根据这M条信息,是否可以确定出A组的队员呢?(默认所有集训队员反映的信息都是符合事实的。)
Input
输入包含多组测试数据。
对于每组测试数据,第一行包含三个正整数N (2<=N<=1000)、K (1<=K<=N)、M (1<=M<=10000),含义同上。接下来M行每行有两个正整数x、y (1<=x, y<=N且x!=y),分别描述了M条信息,对于每对x、y,均表示第x名队员记得第y名队员的排名比自己的要靠后。
Output
Sample Input
3 1 2 1 2 1 3 3 2 2 1 2 1 3
Sample Output
YES NO
数学模型:给定n个未知数,m组关系,没组关系指出n个未知数中的2个的大小关系,例如A>B,求能否据此确定前k个数的集合?
一开始想到并查集,写了好几次都WA了,最后发现此题中的关系并不是等价关系。后来想到一个类似的题,那题也是给定n个未知数和一些大小关系,要求的是能否确定所有数的大小关系。那题是用拓扑排序做的。用拓扑排序的方法很容易找出一组符合条件的解,关键在于如何判断解的唯一性,如果解是唯一的,那么剩下的n-k个数一定在所选的k个数的后面,也就是从这所选的k个数(图中表示为结点)中任意一个出发,可以遍历到剩下的n-k个数。问题也就解决了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 #define N 1001 5 using namespace std; 6 vector<int> g[N]; 7 int n,k,m,cnt,d[N],a[N],vis[N],flag[N]; 8 void dfs(int u) 9 { 10 int i,v; 11 if(flag[u]==0 && vis[u]==0) cnt++; 12 vis[u]=1; 13 for(i=0;i<g[u].size();i++) 14 { 15 v=g[u][i]; 16 if(!vis[v]) dfs(v); 17 } 18 } 19 int main() 20 { 21 int i,u,v; 22 while(~scanf("%d%d%d",&n,&k,&m)) 23 { 24 for(i=1;i<=n;i++) 25 { 26 g[i].clear(); 27 d[i]=0; 28 } 29 for(i=0;i<m;i++) 30 { 31 scanf("%d%d",&u,&v); 32 g[u].push_back(v); 33 d[v]++; 34 } 35 memset(flag,0,sizeof(flag)); 36 cnt=0; 37 while(cnt<k) 38 { 39 for(u=1;u<=n && d[u];u++); 40 d[u]--; 41 a[cnt++]=u; 42 flag[u]=1; 43 for(i=0;i<g[u].size();i++) 44 { 45 v=g[u][i]; 46 d[v]--; 47 } 48 } 49 for(i=0;i<k;i++) 50 { 51 u=a[i]; 52 cnt=0; 53 memset(vis,0,sizeof(vis)); 54 dfs(u); 55 if(cnt!=n-k) break; 56 } 57 if(i<k) puts("NO"); 58 else puts("YES"); 59 } 60 return 0; 61 }