POJ Ikki's Story IV - Panda's Trick (2-SAT,基础)

 

题意:给1个从0~n-1的连续的序列,将其头尾相接围绕成环,玩家想要将其中部分点连线,连线有2种方式,绕到外面或绕在圆内,且每个点最多仅仅有一条连线的可能。如果所给的m条弧必须相交,那么输出1种结果。如果可以有一种方案不相交,输出另一种结果。

思路:

  这题目都是难看清楚的。借用一下别人的图吧,大概就是如下这样的方式。要求不能够相交,比如0-2和1-3同时走圆内必定相交了。

  [poj 3207] Ikkis Story IV - Pandas Trick - Shengrui - --恢弘志士之气,不宜妄自菲薄

分析一下题意先:

  根据所给的m条边可知,假设给的是a-b,那么a-b要么得绕环外,要么只能在环内,除非a和b是连续的点才不会影响到任何弧,否则一定会多少影响其他弧的走势。比如上面所举例 0-2和 1-3,就必须有一条弧选择外环,一个选择内环。

  如何使他们有序不冲突呢?其实这题跟环上的点没多大关系,而且给的点数n也没什么用,而倒是边才重要。我们要做的是让边不冲突。那么肯定是要先选出可能冲突的边来处理,让他们有序而不冲突。

  那么什么时候会冲突?上面举过2次的例子就会冲突了,所以我们要将有可能冲突的边全部找出来,那些连边都没有的点是没有用的,连边都没有,谈什么冲突。

  根据冲突边来建图?若有两条边x和y冲突了,那么一条必须在内,另一条相反。则按照2-SAT的模型,选择了x在内,y则必须在外;选择了y在内,则x必须在外。将x放在环外和环内作为两个点,y也同理,将他们连上对应的有向边 “内x->外y”(可以用2*x和2*x+1这种方式代替环内环外的)。这里是以边作为点的。即每条边有两种可能,但是不能同时选,这和2-SAT模型一样。根据这个模型可以建图了。

  求解方式?建图后用tarjan算法求强连通分量,根据边不能同时在环外和环内的规则,如果发现同时在环外/内就判其实无解的。

 

 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 INF 0x7f7f7f7f

 9 using namespace std;

10 const int N=500+5;

11 vector<int> vect[N*2];

12 int e[N][2];

13 int lowlink[N*2], dfn[N*2], scc_no[N*2], dfn_clock, scc_cnt;

14 stack<int> stac;

15 

16 void DFS(int x)

17 {

18     stac.push(x);

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

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

21     {

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

23         if(!dfn[t])

24         {

25             DFS(t);

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

27         }

28         else if(!scc_no[t])

29             lowlink[x]=min(lowlink[x],dfn[t]);

30     }

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

32     {

33         scc_cnt++;

34         while(true)

35         {

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

37             scc_no[t]=scc_cnt;

38             if(t==x)    break;

39         }

40     }

41 }

42 

43 int cal(int up)

44 {

45     scc_cnt=dfn_clock=0;

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

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

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

49 

50     for(int i=0; i<up; i++)    if(!dfn[i])    DFS(i);   //求强连通分量

51 

52     for(int i=0; i<up; i+=2)    if(scc_no[i]==scc_no[i+1])    return false; //同在一个强连通分量内

53 

54     return true;

55 }

56 

57 

58 int main()

59 {

60     //freopen("input.txt", "r", stdin);

61     //仅有1个测试例子

62     int n, m, a, b;

63 

64     scanf("%d%d",&n,&m);

65     for(int i=0; i<m; i++)

66     {

67         scanf("%d%d",&e[i][0],&e[i][1]);

68         if(e[i][0]>e[i][1]) swap(e[i][0],e[i][1]);  //保证小在前

69     }

70 

71     for(int i=0; i<m; i++)  //穷举每条弧与其他弧的关系:相当于以点建图

72     {

73         a=e[i][0];

74         b=e[i][1];

75         for(int j=i+1; j<m; j++)

76         {

77             int c=e[j][0];

78             int d=e[j][1];

79             if( a<c && c<b && b<d || c<a && a<d && d<b ) //弧冲突了  a<c<b<d || c<a<d<b

80             {

81                 vect[i*2].push_back(j*2+1);    //只有错开才能无冲突

82                 vect[j*2].push_back(i*2+1);

83                 vect[i*2+1].push_back(j*2);

84                 vect[j*2+1].push_back(i*2);

85             }

86         }

87     }

88     if(cal(m*2))    puts("panda is telling the truth...");

89     else         puts("the evil panda is lying again");

90     return 0;

91 }
AC代码

 

你可能感兴趣的:(poj)