题解 P4171 【[JSOI2010]满汉全席】

什么,tarjan?那是什么?

码量太大,我选择放弃

为什么不用dfs写2-sat呢?他会伤心的说

这题2-sat的过程大佬们已经讲得非常清楚了,我就略微提一下,主要讲dfs的原理

2_sat原理

我们知道,如果要求 (a)(b) , 那么如果 (a) 不成立,我们可以保证 (b) 成立.

换成式子: (a||b) = (!a&&b) || (a&&!b)

于是,我们只需要将!a和b连边,!b和a连边就能确定必然要走的路线

dfs原理

dfs的原理是对于每个点,我们将所有能拿的边都拿了,然后判断是否满足某一个点 (a) 必然要使得另外一点要同时满足 (b)(!b) .如果存在这样的一个点,那么这个图必然不成立

具体实现

首先是连边:

for (int i=0;i<m;i++){
      string s1,s2; cin >> s1 >> s2;
      int b = (s1[0]=='m') ? 1 : 0, d = (s2[0]=='m') ? 1 : 0;
      int a = get_num(s1),c = get_num(s2);//get_num是将这个string后面的数字转化为int
      adj[a+((b+1)%2)*n].push_back(c+n*d);
      adj[c+((d+1)%2)*n].push_back(a+b*n);
    }

然后正常dfs

bool dfs(int posi){
  memset(vis,0,sizeof(vis));
  queue<int> q;
  q.push(posi);
  vis[posi] = true;
  while (!q.empty()){
    int qf = q.front();q.pop();
    for (int v : adj[qf]){
      if (!vis[v]){
        vis[v] = true;//没去过下标
        q.push(v);
      }
    }
  }
  for (int i=1;i<=n;i++) if (vis[i] && vis[i+n]) return false;//这里相当于b&!b,因为i+n代表的是!i
  return true;
}

然后呢?

然后就完了啊

其实就对每个点判断是否成立就好了

完整代码:

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <math.h>
using namespace std;
#define pp pair<int,int>
int n,m,pos[100005];
vector<int> adj[200005];
bool vis[200005];
bool dfs(int posi){
  memset(vis,0,sizeof(vis));
  queue<int> q;
  q.push(posi);
  vis[posi] = true;
  while (!q.empty()){
    int qf = q.front();q.pop();
    for (int v : adj[qf]){
      if (!vis[v]){
        vis[v] = true;
        q.push(v);
      }
    }
  }
  for (int i=1;i<=n;i++) if (vis[i] && vis[i+n]) return false;
  return true;
}
int get_num(string s){
  int tmp = 0;
  for (int i=s.length()-1;i>=1;i--){
    tmp+=(int)(s[i]-48)*pow(10,(int)s.length()-i-1);
  }//从右往左拿数
  return tmp;
}
int main(){
  int T; cin >> T;
  while(T--){

    cin >> n >> m;
    for (int i=1;i<=n*2;i++) adj[i].clear();
    for (int i=0;i<m;i++){
      string s1,s2; cin >> s1 >> s2;
      int b = (s1[0]=='m') ? 1 : 0, d = (s2[0]=='m') ? 1 : 0;//满汉的情况下b是1,否则是2
      int a = get_num(s1),c = get_num(s2);
      adj[a+((b+1)%2)*n].push_back(c+n*d);//a+((b+1)%2)*n表示!a
      adj[c+((d+1)%2)*n].push_back(a+b*n);
    }
    for (int i=1;i<=n;i++) { if(!dfs(i) && !dfs(i+n)){ cout << "BAD" << endl;goto abcd;//有一个跑不了就输出BAD然后走人} }
    cout << "GOOD" << endl;//到这还没走人就是可以输出
    abcd:;
  }

}

好题(双倍经验?):P3007

建议刷完这题去写,难度基本上一样,加几行多一题紫题不好么

原文地址:https://www.cnblogs.com/DannyXu/p/12299521.html