POJ Priest John's Busiest Day (2-SAT,常规)

 

 

题意:

  一些人要在同一天进行婚礼,但是牧师只有1个,每一对夫妻都有一个时间范围[s , e]可供牧师选择,且起码要m分钟才主持完毕,但是要么就在 s 就开始,要么就主持到刚好 e 结束。因为人数太多了,这些时间可能会重叠,可能还会完全包含,可能还没有交叉,各种情况。问牧师能否主持完全部人的婚礼,若可以,给出每对夫妻占用牧师的一个时间段(记得按所给的夫妻的顺序哦)。

 

 

  主要步骤如下。

(1)先建原图,不管是否会冲突。

(2)找强连通分量来缩点,如果会冲突,这个时候应该发现。

(3)建个缩点后,原图的反向图。

(4)着色法标记所要输出的时间段。

(5)时间段按照所给每对夫妻的顺序来输出。

 

实现:

  (1)婚礼要么在s就开始,要么在e刚好结束,还有可能这对夫妻就要求从s-e都要主持呢。 先把时间给转换成分钟,每对夫妻拥有2个时间段。

  (2)接下来主要是建图,建图时只考虑“冲突”,即如果i*2和j*2冲突了,那么选i*2就必须选j*2+1。先不用顾着i*2和j*2+1是否冲突,只要将4种可能的组合列出来,建图,其他的交给强连通分量去做。

  (3)强连通分量承接第3步的重任,在求强连通分量时要顺便判断是否会冲突,当然这比较简单。

  (4)建反向图只是穷举原图中的每条边,判断是否在同个scc中,如果不是就可以建边了,将边反过来这么简单。

  (5)着色时还得将另外冲突的“全部“着其他颜色,那么根据已建好的反向图一直DFS下去,如果已经着色,可以退出了,别每次都深搜到底了,没必要。

  (6)时间段还得按所给夫妻顺序!那得再稍微处理一下。

 

 

 

  1 #include <iostream>

  2 #include <stdio.h>

  3 #include <string.h>

  4 #include <vector>

  5 #include <stack>

  6 #include <algorithm>

  7 #define LL long long

  8 #define pii pair<int,int>

  9 #define INF 0x7f7f7f7f

 10 using namespace std;

 11 const int N=1000+5;

 12 int t1[N], t2[N], t3[N], t4[N]; //保存时间点

 13 bool vis[N];

 14 vector<int> vect[N*2], rg[N*2], scc[N*2];   //分别是:原图,反向图,

 15 stack<int> stac;    //强连通分量必备

 16 int lowlink[N*2], dfn[N*2], scc_no[N*2], scc_cnt, dfn_clock;    //强连通分量必备

 17 int col[N*2];   //用于着色

 18 bool dele[N*2]; //标记输出点

 19 

 20 inline int to_time(int h, int m){return h*60+m;}

 21 inline bool conflict(int a,int b, int c, int d){if(b<=c || d<=a)    return true;return false;}

 22 

 23 void get_graph(int n)   //难在建图吧?

 24 {

 25     for(int i=0; i<n*2; i++)  vect[i].clear();

 26     for(int i=0; i<n; i++)

 27     {

 28         for(int j=0; j<n; j++)        //考虑放在前面

 29         {

 30             if(i==j) continue;

 31             if(!conflict(t1[i],t2[i], t1[j],t2[j]))  vect[i*2].push_back(j*2+1);

 32             if(!conflict(t1[i],t2[i], t3[j],t4[j]))  vect[i*2].push_back(j*2);

 33 

 34             if(!conflict(t3[i],t4[i], t1[j],t2[j]))  vect[i*2+1].push_back(j*2+1);

 35             if(!conflict(t3[i],t4[i], t3[j],t4[j]))  vect[i*2+1].push_back(j*2);

 36         }

 37     }

 38 }

 39 

 40 void DFS(int x) //模板tarjan

 41 {

 42     stac.push(x);

 43     dfn[x]=lowlink[x]=++dfn_clock;

 44     for(int i=0; i<vect[x].size(); i++)

 45     {

 46         int t=vect[x][i];

 47         if(!dfn[t])

 48         {

 49             DFS(t);

 50             lowlink[x]=min(lowlink[x],lowlink[t]);

 51         }

 52         else if(!scc_no[t]) lowlink[x]=min(lowlink[x],dfn[t]);

 53     }

 54     if(lowlink[x]==dfn[x])

 55     {

 56         scc[++scc_cnt].clear();

 57         while(true)

 58         {

 59             int t=stac.top();stac.pop();

 60             scc_no[t]=scc_cnt;

 61             scc[scc_cnt].push_back(t);

 62             if(t==x)    break;

 63         }

 64     }

 65 }

 66 

 67 int find_scc(int n) //求强连通分量,顺便检查是否满足条件

 68 {

 69     scc_cnt=dfn_clock=0;

 70     memset(lowlink,0,sizeof(lowlink));

 71     memset(dfn,0,sizeof(dfn));

 72     memset(scc_no,0,sizeof(scc_no));

 73     for(int i=0; i<n; i++)  if(!dfn[i]) DFS(i);

 74     for(int i=0; i<n; i+=2) if(scc_no[i]==scc_no[i+1])  return false;   //检查是否冲突

 75     return true;

 76 }

 77 

 78 void build_rg(int n)    //建反向图,为了要反向着色

 79 {

 80     for(int i=0; i<n; i++)  rg[i].clear();

 81 

 82     for(int i=0; i<n; i++)

 83     {

 84         for(int j=0; j<vect[i].size(); j++)

 85         {

 86             int t=vect[i][j];

 87             if(scc_no[i]!=scc_no[t])    //不属于同个强连通分量

 88                 rg[scc_no[t]].push_back(scc_no[i]);

 89         }

 90     }

 91 }

 92 

 93 void del(int s)     //删除的是反向边,之前已建好的rg图

 94 {

 95     while(!col[s])

 96     {

 97         col[s]=3;

 98         for(int i=0; i<rg[s].size(); i++)    del(rg[s][i]); //递归“删除”,按反向边的方向(即着其他颜色)

 99     }

100 }

101 

102 void color()

103 {

104     memset(col,0,sizeof(col));

105     for(int i=1; i<=scc_cnt; i++)   //没有按照拓扑顺序也能AC???

106         if(!col[i])

107         {

108             col[i]=2;

109             del( scc_no[ scc[i][0]%2==0?scc[i][0]+1:scc[i][0]-1]);    //在第i个SCC中任取1点,取其对立点,再取其所在scc编号,进行着色

110         }

111 }

112 

113 void print(int n)

114 {

115     memset(dele,0,sizeof(dele));

116     puts("YES");

117     for(int i=1; i<scc_cnt; i++)

118     {

119         if(col[i]==2)   //col=2的都是要的,当然,选全部col=3也一样,因为对称

120             for(int j=0; j<scc[i].size(); j++)

121                 dele[scc[i][j]  ]=true; //把所有要输出的点先标记出来

122     }

123     for(int i=0; i<n; i++)

124     {

125         if(dele[i])

126             if(i%2==0)  //取前段

127             {

128                 int a=t1[i/2]/60;

129                 int b=t1[i/2]%60;

130                 int c=t2[i/2]/60;

131                 int d=t2[i/2]%60;

132                 printf("%02d:%02d %02d:%02d\n",a,b,c,d);

133             }

134             else        //取后段

135             {

136                 int a=t3[i/2]/60;

137                 int b=t3[i/2]%60;

138                 int c=t4[i/2]/60;

139                 int d=t4[i/2]%60;

140                 printf("%02d:%02d %02d:%02d\n",a,b,c,d);

141             }

142     }

143 }

144 int main()

145 {

146     freopen("input.txt", "r", stdin);

147     int n, a, b, c, d, e, f, g;

148     while(~scanf("%d",&n))

149     {

150         memset(t1,0,sizeof(t1));

151         memset(t2,0,sizeof(t2));

152         memset(t3,0,sizeof(t3));

153         memset(t4,0,sizeof(t4));

154         for(int i=0; i<n; i++)

155         {

156             scanf("%d %c %d %d %c %d %d",&a,&b,&c,  &d,&e,&f,  &g);

157             t1[i]=to_time(a,c); //时间转成分钟,分别有2种,t1t2表示在前的start和end,t3t4表示在后

158             t2[i]=to_time(a,c)+g;

159             t3[i]=to_time(d,f)-g;

160             t4[i]=to_time(d,f);

161         }

162         get_graph(n);

163         if(!find_scc(n<<1))    {puts("NO");continue;}

164         build_rg(n*2);          //按缩点建新的反向图reverse_graph

165         color();                //着色完,所需要的点也就出结果了

166         print(n*2);

167     }

168     return 0;

169 }
AC代码

 

你可能感兴趣的:(poj)