拓补排序
在图中如果一个点被另一个点覆盖,或者一个点成立的前提是另一个点存在,那么他们成图后的关系是拓补排序。
训练题:
Window Pains (poj-2585)
题意:有九个窗口,每个窗口占四格:
然后将会任意打开多个窗口,给出打开多个窗口后的屏幕。判断屏幕呈现的数字是否是打开任意窗口后呈现的样子。
解题思路:
判断这个格原来的数字,如果这个格的数字不是原来的数字,可以知道每个数字所组成的小四方格的左上角位置为该数字i:x=(i-1)/3,y=(i-1)%3,那么说明现在这个数在所在的区域覆盖了这个格原来的数字所覆盖的区域。然后在用拓补排序即可。
代码如下:
1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 #include <vector> 5 #include <string.h> 6 #include <string> 7 using namespace std; 8 vector<int> mp[1000]; 9 int degree[1000]; 10 int arr[4][4];//存屏幕 11 int main(){ 12 string s; 13 int x,y,num; 14 while(cin>>s&&s=="START"){ 15 queue<int>que; 16 num=0; 17 for(int i=1;i<=9;i++){ 18 mp[i].clear(); 19 } 20 memset(degree,0,sizeof(degree)); 21 for(int i=0;i<4;i++){ 22 for(int j=0;j<4;j++){ 23 cin>>arr[i][j]; 24 } 25 } 26 for(int i=1;i<=9;i++){ 27 x=(i-1)/3;//找i点所在的首位置 28 y=(i-1)%3; 29 for(int j=0;j<2;j++){ 30 for(int k=0;k<2;k++){//再找其他3个位置 31 if(arr[x+j][y+k]!=i){ 32 mp[arr[x+j][y+k]].push_back(i);//拓扑排序即可 33 degree[i]++; 34 } 35 } 36 } 37 } 38 for(int i=1;i<=9;i++){ 39 if(degree[i]==0){ 40 que.push(i); 41 num++; 42 } 43 } 44 int c; 45 while(!que.empty()){ 46 c=que.front(); 47 que.pop(); 48 for(int i=0;i<mp[c].size();i++){ 49 degree[mp[c][i]]--; 50 if(degree[mp[c][i]]==0){ 51 que.push(mp[c][i]); 52 num++; 53 } 54 } 55 } 56 cin>>s; 57 if(num==9){//每个数字都将有覆盖或被覆盖所以如果揭开完num=9; 58 puts("THESE WINDOWS ARE CLEAN"); 59 } 60 else{ 61 puts("THESE WINDOWS ARE BROKEN"); 62 } 63 64 } 65 return 0; 66 }
Rank of Tetris (hdu-1811)
自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球。
为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜,定时更新,名堂要比福布斯富豪榜还响。关于如何排名,这个不用说都知道是根据Rating从高到低来排,如果两个人具有相同的Rating,那就按这几个人的RP从高到低来排。
终于,Lele要开始行动了,对N个人进行排名。为了方便起见,每个人都已经被编号,分别从0到N-1,并且编号越大,RP就越高。
同时Lele从狗仔队里取得一些(M个)关于Rating的信息。这些信息可能有三种情况,分别是"A
> B","A = B","A < B",分别表示A的Rating高于B,等于B,小于B。
现在Lele并不是让你来帮他制作这个高手榜,他只是想知道,根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"。
Input
本题目包含多组测试,请处理到文件结束。
每组测试第一行包含两个整数N,M(0<=N<=10000,0<=M<=20000),分别表示要排名的人数以及得到的关系数。
接下来有M行,分别表示这些关系
Output
对于每组测试,在一行里按题目要求输出
Sample Input
3 3
0 > 1
1 < 2
0 > 2
4 4
1 = 2
1 > 3
2 > 0
0 > 1
3 3
1 > 0
1 > 2
2 < 1
Sample Output
OK
CONFLICT
UNCERTAIN
解题思路:
当rating值相同时可以用并查集合并,其他的在用拓补排序,最后判断加入队列的数是不是并查集中根的总数。
代码:
1 #include <iostream> 2 #include <queue> 3 #include <vector> 4 #include <string.h> 5 using namespace std; 6 7 vector<int> mp[10010]; 8 int degree[10010]; 9 int pre[10010]; 10 int flag,flags; 11 12 int find(int a){//并查集 13 if(pre[a]==a){ 14 return a; 15 } 16 return pre[a]=find(pre[a]); 17 } 18 19 void join(int a,int b){ 20 int aa=find(a),bb=find(b); 21 if(aa!=bb){ 22 pre[aa]=bb; 23 } 24 return ; 25 } 26 27 int main(){ 28 int n,m,a[10010],b[10010],c; 29 char s[10010]; 30 while(cin>>n>>m&&n){ 31 flags=0; 32 flag=0; 33 for(int i=1;i<=n;i++){ 34 pre[i]=i; 35 } 36 queue<int>que; 37 for(int i=1;i<=n;i++){ 38 mp[i].clear(); 39 } 40 memset(degree,0,sizeof(degree)); 41 42 for(int i=1;i<=m;i++){ 43 cin>>a[i]>>s[i]>>b[i]; 44 a[i]++,b[i]++; 45 if(s[i]=='='){//如果等于=号那么将他们合并,不将其他的直接加入存图数组中,因为会有儿子结点。 46 join(a[i],b[i]); 47 } 48 } 49 for(int i=1;i<=m;i++){ 50 int aa=find(a[i]),bb=find(b[i]); 51 if(s[i]=='>'){ 52 mp[aa].push_back(bb);//将他们转化为根加入到存图数组中 53 degree[bb]++; 54 } 55 else if(s[i]=='<'){ 56 mp[bb].push_back(aa); 57 degree[aa]++; 58 } 59 } 60 for(int i=1;i<=n;i++){ 61 if(degree[i]==0&&pre[i]==i){ 62 que.push(i); 63 flag++; 64 } 65 } 66 while(!que.empty()){ 67 if(que.size()>1){ 68 flags=1; 69 } 70 c=que.front(); 71 que.pop(); 72 for(int j=0;j<mp[c].size();j++){ 73 int i=mp[c][j]; 74 degree[i]--; 75 if(degree[i]==0){ 76 que.push(i); 77 flag++; 78 } 79 } 80 } 81 for(int i=1;i<=n;i++){ 82 if(pre[i]==i){ 83 flag--;//存入队列中的个数是不是等于并查集跟的个数 84 } 85 } 86 if(flag){ 87 puts("CONFLICT"); 88 } 89 else if(flags){ 90 puts("UNCERTAIN"); 91 } 92 else{ 93 puts("OK"); 94 } 95 } 96 return 0; 97 }
Reward (hdu-2647)
Dandelion's uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards.
The workers will compare their rewards ,and some one may have demands of the
distributing of rewards ,just like a's reward should more than b's.Dandelion's
unclue wants to fulfill all the demands, of course ,he wants to use the least
money.Every work's reward will be at least 888 , because it's a lucky number.
Input
One line with two integers n and m ,stands
for the number of works and the number of demands
.(n<=10000,m<=20000)
then m lines ,each line contains two integers a and b ,stands for a's reward
should be more than b's.
Output
For every case ,print the least money dandelion 's uncle needs to distribute .If it's impossible to fulfill all the works' demands ,print -1.
Sample Input
2 1
1 2
2 2
1 2
2 1
Sample Output
1777
-1
题意:
在发奖金的时候,有的人希望比其他某个人高,所以你要判断满足所有人的要求下你要发的所有奖金。每个人的奖金不小于888。如果无法全部满足就输出-1。
解题思路:
用奖金少的覆盖奖金多的,即谁大谁被覆盖。然后可以知道被覆盖的比覆盖他的多1,没被覆盖的为初始奖金888。然后在全部相加即可得到要付的最大奖金,如果过程中出现环,那么就无法继续,输出-1。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 vector<int> mp[10010]; 4 int degree[10010]; 5 int sum; 6 int reward[10010];//存取每个人的奖金 7 int main(){ 8 int n,m,a,b,c,flag; 9 while(cin>>n>>m){ 10 queue<int>que; 11 flag=0; 12 sum=0; 13 for(int i=1;i<=n;i++){ 14 mp[i].clear(); 15 } 16 memset(reward,0,sizeof(reward)); 17 memset(degree,0,sizeof(degree));//初始化 18 for(int i=1;i<=m;i++){ 19 cin>>b>>a; 20 mp[a].push_back(b);//数据量过大所以用vector存图 21 degree[b]++;//改点度数+1 22 } 23 for(int i=1;i<=n;i++){ 24 if(degree[i]==0){ 25 que.push(i); 26 reward[i]=888;//初始没被覆盖的人的奖金 27 flag++;//代表这个人不在不覆盖 28 } 29 } 30 while(!que.empty()){ 31 c=que.front(); 32 que.pop(); 33 for(int j=0;j<mp[c].size();j++){ 34 int i=mp[c][j]; 35 degree[i]--;//度数减去一 36 if(degree[i]==0){ 37 reward[i]=reward[c]+1;//奖金比覆盖他的人多1. 38 que.push(i); 39 flag++; 40 } 41 } 42 } 43 if(flag==n){//如果每个人的需求都被满足,即没成环 44 for(int i=1;i<=n;i++){ 45 sum+=reward[i];// 总共的奖金 46 } 47 cout<<sum<<endl; 48 } 49 else{ 50 puts("-1"); 51 } 52 } 53 return 0; 54 }