什么,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
建议刷完这题去写,难度基本上一样,加几行多一题紫题不好么