算法笔记--2-sat
强连通分量的应用,详见《挑战程序设计》P324
模板(2019.7):
namespace two_sat {
int dfn[M*], low[M*], cnt, stk[M*], top, cmp[M*], tot, n;
bool vis[M*];
vector<int> g[M*];
void init(int sz) {
n = sz;
}
void add(int u, int v) {
g[u].pb(v);
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
stk[++top] = u;
vis[u] = true;
for (int v : g[u]) {
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
cmp[u] = ++tot;
while(stk[top] != u) cmp[stk[top]] = tot, vis[stk[top--]] = false;
vis[stk[top--]] = false;
}
}
bool ck() {
for (int i = ; i <= *n; ++i) if(!dfn[i]) tarjan(i);
for (int i = ; i <= n; ++i) {
if(cmp[i] == cmp[i+n]) return false;
}
return true;
}
}
思路:强连通分量分解,看有没有两个同一个国家的代表在一个强连通分量里,如果有,就是NIE。这个不是关键,关键是怎么输出,输出还要用一下dfs,把所有能到达的点标记一下,顺便判断一下和之前有没有矛盾,有矛盾的话所有被标记的点又要重新标记回去。其实这道题可以不用强连通分量分解,直接dfs。
代码1(强连通分量分解+dfs):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) int n,m,u,v;
const int N=2e4+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
bool vis[N];
bool vis1[N];
int cmp[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); mem(vis,false);
int k=;
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
void init()
{
for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
}
bool DFS(int u)
{
vis[u]=true;
vis1[u]=true;
if(vis[u^])return false;
for(int i=;i<g[u].size();i++)
{
if(!vis[g[u][i]]&&!DFS(g[u][i]))return false;
}
return true;
}
void red(int u)
{
vis1[u]=false;
vis[u]=false;
for(int i=;i<g[u].size();i++)
{
if(vis1[g[u][i]])red(g[u][i]);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
while(cin>>n>>m)
{
init();
for(int i=;i<m;i++)
{
cin>>u>>v;
u--;
v--;
add_edge(u,v^);
add_edge(v,u^);
}
int t=scc();
bool flag=false;
for(int i=;i<*n;i+=)if(cmp[i]==cmp[i+]){cout<<"NIE"<<endl;flag=true;break;}
if(flag)continue;
mem(vis,false);
mem(vis1,false);
for(int i=;i<*n;i+=)
{
if(vis[i])
{
cout<<i+<<endl;
continue;
}
else if(vis[i+])
{
cout<<i+<<endl;
continue;
}
else
{
if(DFS(i))
{
cout<<i+<<endl;
}
else
{
red(i);
cout<<i+<<endl;
DFS(i+);
}
}
}
}
return ;
}
代码2(dfs):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) int n,m,u,v,tot;
const int N=2e4+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
bool vis[N];
int cmp[N];
int s[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
/*void dfs(int u)
{
vis[u]=true;
for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=0;i<2*n;i++)if(!vis[i])dfs(i); mem(vis,false);
int k=0;
for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}*/
void init()
{
for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
}
bool DFS(int u)
{
if(vis[u^])return false;
if(vis[u])return true;
vis[u]=true;
s[tot++]=u;
for(int i=;i<g[u].size();i++)
{
if(!DFS(g[u][i]))return false;
}
return true;
}
bool solve()
{
mem(vis,false);
for(int i=;i<*n;i+=)
{
if(vis[i]||vis[i^])continue;
tot=;
if(!DFS(i))
{
while(tot)vis[s[--tot]]=false;
if(!DFS(i^))return false;
}
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
while(cin>>n>>m)
{
init();
for(int i=;i<m;i++)
{
cin>>u>>v;
u--;
v--;
add_edge(u,v^);
add_edge(v,u^);
}
//int t=scc();
if(solve())
{
for(int i=;i<*n;i++)
if(vis[i])cout<<i+<<endl;
}
else cout<<"NIE"<<endl;
}
return ;
}
例题2:POJ 3207 Ikki's Story IV - Panda's Trick
思路:由于题目说每个点最多只能连一次,所以我直接用起点的坐标映射成它在圆内,+n后映射成它在圆外。不过这样求强连通时就要遍历每个点了,有点慢。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) const int N=2e3+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
vector<int>s;
bool vis[N];
int cmp[N];
int n,m,u,v;
int a[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); int k=;
mem(vis,false);
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
mem(a,-);
cin>>n>>m;
for(int i=;i<m;i++)
{
cin>>u>>v;
a[u]=v;
a[v]=u;
s.pb(u);
s.pb(v);
} sort(s.begin(),s.end());
for(int i=;i<s.size();i++)
{
for(int j=i+;j<s.size();j++)
{
int l=min(s[i],a[s[i]]),r=max(s[i],a[s[i]]);
int _l=min(s[j],a[s[j]]),_r=max(s[j],a[s[j]]);
if((r>_l&&r<_r&&l<_l)||(l<_r&&l>_l&&r>_r))
{
add_edge(s[i],s[j]+n);
add_edge(s[j],s[i]+n);
add_edge(s[i]+n,s[j]);
add_edge(s[j]+n,s[i]);
}
}
} int t=scc();
for(int i=;i<s.size();i++)
if(cmp[s[i]]==cmp[s[i]+n])
{
cout<<"the evil panda is lying again"<<endl;
return ;
}
cout<<"panda is telling the truth..."<<endl;
return ;
}
例题3:POJ 3683 Priest John's Busiest Day
思路:大白书上的是输出拓扑序大的,不懂,留坑。
补坑:拓扑序大的没有边连向拓扑序小的强联通,所以不会产生矛盾。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) const int N=2e3+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
bool vis[N];
int cmp[N];
int n,m,u,v;
int S[N],T[N],D[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); int k=;
mem(vis,false);
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
void init()
{
for(int i=;i<=n;i++)g[i].clear(),rg[i].clear();
}
void solve()
{
mem(vis,false);
}
int main()
{
int a,b,c,d;
while(~scanf("%d",&n))
{
init();
for(int i=;i<n;i++)
{
scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&D[i]);
S[i]=a*+b;
T[i]=c*+d;
}
for(int i=;i<n;i++)
{
for(int j=i+;j<n;j++)
{
if(min(S[i]+D[i],S[j]+D[j])>max(S[i],S[j]))add_edge(i,n+j),add_edge(j,n+i);
if(min(T[i],T[j])>max(T[i]-D[i],T[j]-D[j]))add_edge(n+j,i),add_edge(n+i,j);
if(min(T[i],S[j]+D[j])>max(T[i]-D[i],S[j]))add_edge(n+i,n+j),add_edge(j,i);
if(min(T[j],S[i]+D[i])>max(T[j]-D[j],S[i]))add_edge(i,j),add_edge(n+j,n+i);
}
}
int t=scc();
bool flag=false;
for(int i=;i<n;i++)if(cmp[i]==cmp[i+n]){
flag=true;
break;
}
if(flag)printf("NO\n");
else
{
printf("YES\n");
for(int i=;i<n;i++)
{
if(cmp[i]>cmp[n+i])
printf("%02d:%02d %02d:%02d\n",S[i]/,S[i]%,(S[i]+D[i])/,(S[i]+D[i])%);
else printf("%02d:%02d %02d:%02d\n",(T[i]-D[i])/,(T[i]-D[i])%,T[i]/,T[i]%);
}
}
}
return ;
}
思路:一开始看起来很复杂,其实只要建好边就好了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) const int N=2e3+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
vector<int>s;
bool vis[N];
int cmp[N];
int n,m,u,v;
int a[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); int k=;
mem(vis,false);
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
void init()
{
for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
int a;
string t;
while(cin>>n>>m)
{
for(int i=;i<m;i++)
{
cin>>u>>v>>a>>t;
if(t[]=='A')
{
if(a==)
{
add_edge(u,v);
add_edge(v,u);
add_edge(u+n,u);
add_edge(v+n,v);
}
else
{
add_edge(u,v+n);
add_edge(v,u+n);
}
}
else if(t[]=='O')
{
if(a==)
{
add_edge(u+n,v);
add_edge(v+n,u);
}
else
{
add_edge(u+n,v+n);
add_edge(v+n,u+n);
add_edge(u,u+n);
add_edge(v,v+n);
}
}
else if(t[]=='X')
{
if(a==)
{
add_edge(u,v+n);
add_edge(v,u+n);
add_edge(v+n,u);
add_edge(u+n,v);
}
else
{
add_edge(u,v);
add_edge(v,u);
add_edge(v+n,u+n);
add_edge(u+n,v+n);
}
}
}
scc();
bool flag=false;
for(int i=;i<n;i++)
if(cmp[i]==cmp[i+n]){
flag=true;
break;
}
if(flag)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return ;
}
例题5:POJ 3648 Wedding
思路:与例1相同,不过要建一条0号新娘和他新郎的边,这样只会选出新郎那一边的人。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a)) const int N=;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
bool vis[N];
int cmp[N];
int s[N];
int n,m,u,v;
int tot=;
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); mem(vis,false);
int k=;
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
void init()
{
for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
}
bool DFS(int u)
{
if(u<n&&vis[u+n])return false;
if(u>=n&&vis[u-n])return false;
if(vis[u])return true;
vis[u]=true;
s[tot++]=u;
for(int i=;i<g[u].size();i++)
{
if(!DFS(g[u][i]))return false;
}
return true;
}
bool solve()
{
mem(vis,false);
for(int i=;i<*n;i++)
{
if(vis[i])continue;
if(i>=n&&vis[i-n])continue;
if(i<n&&vis[i+n])continue;
tot=;
if(!DFS(i))
{
while(tot)vis[s[--tot]]=false;
if(i<n&&!DFS(i+n))return false;
if(i>=n&&!DFS(i-n))return false;
}
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
string s1,s2;
while(cin>>n>>m)
{
if(n==&&m==)break;
init();
for(int i=;i<m;i++)
{
cin>>s1>>s2;
int t=;
for(int j=;j<s1.size();j++)
{
if(''<=s1[j]&&s1[j]<='')t=t*+s1[j]-'';
else
{
if(s1[j]=='w')
{
u=t;
}
else
{
u=n+t;
}
}
}
t=;
for(int j=;j<s2.size();j++)
{
if(''<=s2[j]&&s2[j]<='')t=t*+s2[j]-'';
else
{
if(s2[j]=='w')
{
v=t;
}
else
{
v=n+t;
}
}
}
if(v<n)add_edge(u,v+n);
else add_edge(u,v-n);
if(u<n)add_edge(v,u+n);
else add_edge(v,u-n);
}
add_edge(,n);
int t=scc();
bool flag=false;
for(int i=;i<n;i++)
{
if(cmp[i]==cmp[i+n])
{
flag=true;
break;
}
}
if(flag)
{
cout<<"bad luck"<<endl;
}
else
{
tot=;
mem(vis,false);
solve();
for(int i=;i<n;i++)
{
if(vis[i])cout<<i<<"h";
else cout<<i<<"w";
if(i!=n-)cout<<' ';
}
cout<<endl;
}
}
return ;
}
例题6:Codeforces 468B - Two Sets
思路:如果x在a集合中,那么a-x不存在的话,x一定在b集合(x在a==>x在b);如果a-x存在,那么有(原命题:x在a==>a-x在a,逆否命题:a-x在b==>x在b),同理,x在b集合中也是一样的。建边建好了就可以输出拓扑序大的了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
const int N=2e5+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
int belong[N]={};
bool vis[N];
int cmp[N];
int s[N];
int n;
int tot=;
struct node
{
int v,id;
bool operator < (node t)const
{
return v<t.v;
}
}a[N];
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<*n;i++)if(!vis[i])dfs(i); mem(vis,false);
int k=;
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
/*bool DFS(int u)
{
if(u<n&&vis[u+n])return false;
if(u>=n&&vis[u-n])return false;
if(vis[u])return true;
vis[u]=true;
s[tot++]=u;
for(int i=0;i<g[u].size();i++)
{
if(!DFS(g[u][i]))return false;
}
return true;
}
bool solve()
{
mem(vis,false);
for(int i=0;i<2*n;i++)
{
if(vis[i])continue;
if(i>=n&&vis[i-n])continue;
if(i<n&&vis[i+n])continue;
tot=0;
if(!DFS(i))
{
while(tot)vis[s[--tot]]=false;
if(i<n&&!DFS(i+n))return false;
if(i>=n&&!DFS(i-n))return false;
}
}
return true;
} */
int main()
{
ios::sync_with_stdio(false);
cin.tie();
int A,B;
cin>>n>>A>>B;
for(int i=;i<n;i++)cin>>a[i].v,a[i].id=i;
sort(a,a+n); for(int i=;i<n;i++)
{
//bool flag=false;
int t=lower_bound(a,a+n,node{A-a[i].v,})-a;
if(t!=n&&a[t].v==A-a[i].v)
{
//flag=true;
add_edge(a[i].id,a[t].id);
add_edge(a[t].id+n,a[i].id+n);
}
else
{
add_edge(a[i].id,a[i].id+n);
}
t=lower_bound(a,a+n,node{B-a[i].v,})-a;
if(t!=n&&a[t].v==B-a[i].v)
{
//flag=true;
add_edge(a[i].id+n,a[t].id+n);
add_edge(a[t].id,a[i].id);
}
else
{
add_edge(a[i].id+n,a[i].id);
}
/*if(!flag)
{
cout<<"NO"<<endl;
return 0;
}*/
}
int t=scc();
for(int i=;i<n;i++)
if(cmp[i]==cmp[i+n])
{
cout<<"NO"<<endl;
return ;
}
cout<<"YES"<<endl;
//solve();
for(int i=;i<n;i++)if(cmp[i]>cmp[i+n])cout<<<<' ';else cout<<<<' ';
cout<<endl;
return ;
}
代码复杂了,因为所有数不同,所以数可以直接映射成下标。
例题7:Codeforces 867E National Property
思路:学会建矛盾边(自己的叫法)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
const int N=2e5+;
vector<int>g[N];
vector<int>rg[N];
vector<int>vs;
vector<int>a[N];
bool vis[N];
int cmp[N];
int n,m;
void add_edge(int u,int v)
{
g[u].pb(v);
rg[v].pb(u);
}
void dfs(int u)
{
vis[u]=true;
for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
vs.pb(u);
}
void rdfs(int u,int k)
{
vis[u]=true;
cmp[u]=k;
for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
}
int scc()
{
mem(vis,false);
vs.clear();
for(int i=;i<=*m;i++)if(!vis[i])dfs(i); mem(vis,false);
int k=;
for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
return k;
}
void init()
{
for(int i=;i<=*m;i++)g[i].clear(),rg[i].clear();
for(int i=;i<n;i++)a[i].clear();
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
int t,b;
while(cin>>n>>m)
{
init();
for(int i=;i<n;i++)
{
cin>>t;
for(int j=;j<t;j++)cin>>b,a[i].pb(b);
}
bool f=false;
for(int i=;i<n-;i++)
{
//cout<<a[i].size()<<endl;
if(a[i].size()<=a[i+].size())
{
for(int j=;j<a[i].size();j++)
{
if(a[i][j]<a[i+][j])
{
add_edge(a[i][j],a[i+][j]);
add_edge(a[i+][j]+m,a[i][j]+m);
break;
}
else if(a[i][j]>a[i+][j])
{
add_edge(a[i][j],a[i][j]+m);
add_edge(a[i+][j]+m,a[i+][j]);
add_edge(a[i][j]+m,a[i+][j]);
add_edge(a[i+][j],a[i][j]+m);
break;
}
}
}
else
{
bool flag=false;
for(int j=;j<a[i+].size();j++)
{
if(a[i][j]<a[i+][j])
{
flag=true;
add_edge(a[i][j],a[i+][j]);
add_edge(a[i+][j]+m,a[i][j]+m);
break;
}
else if(a[i][j]>a[i+][j])
{
flag=true;
add_edge(a[i][j],a[i][j]+m);
add_edge(a[i+][j]+m,a[i+][j]);
add_edge(a[i][j]+m,a[i+][j]);
add_edge(a[i+][j],a[i][j]+m);
break;
}
}
if(!flag)
{
f=true;
break;
}
}
}
if(f)cout<<"No"<<endl;
else
{
//cout<<1<<endl;
scc();
for(int i=;i<=m;i++)
{
if(cmp[i]==cmp[i+m])
{
f=true;
// cout<<i<<endl;
break;
}
}
if(f)cout<<"No"<<endl;
else
{
cout<<"Yes"<<endl;
int cnt=;
for(int i=;i<=m;i++)
if(cmp[i]<cmp[i+m])cnt++;
cout<<cnt<<endl;
for(int i=;i<=m;i++)if(cmp[i]<cmp[i+m])cout<<i<<' ';
cout<<endl;
}
}
}
return ;
}
总结:
关于建边:如果x一定不能选,那么建一条x连向!x的边。
关于输出:2-sat问题的输出如果没有限制条件,那么直接输出拓扑序大的一组答案,如果有限制条件,DFS染色标记后再输出答案。
算法笔记--2-sat的更多相关文章
- 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)
Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...
- 算法笔记--数位dp
算法笔记 这个博客写的不错:http://blog.csdn.net/wust_zzwh/article/details/52100392 数位dp的精髓是不同情况下sta变量的设置. 模板: ]; ...
- 算法笔记--lca倍增算法
算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...
- 算法笔记--STL中的各种遍历及查找(待增)
算法笔记 map: map<string,int> m; map<string,int>::iterator it;//auto it it = m.begin(); whil ...
- 算法笔记--priority_queue
算法笔记 priority_queue<int>que;//默认大顶堆 或者写作:priority_queue<int,vector<int>,less<int&g ...
- 算法笔记--sg函数详解及其模板
算法笔记 参考资料:https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html sg函数大神详解:http://blog.csdn.net/l ...
- 算法笔记——C/C++语言基础篇(已完结)
开始系统学习算法,希望自己能够坚持下去,期间会把常用到的算法写进此博客,便于以后复习,同时希望能够给初学者提供一定的帮助,手敲难免存在错误,欢迎评论指正,共同学习.博客也可能会引用别人写的代码,如有引 ...
- 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...
- 算法笔记(c++)--回文
算法笔记(c++)--回文 #include<iostream> #include<algorithm> #include<vector> using namesp ...
- 算法笔记(c++)--完全背包问题
算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1 价值为1,2,3,4,5 背包容量为10 # ...
随机推荐
- 【环境变量】Linux 下三种方式设置环境变量
1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件,类似的错误. 2.那么什么是环境变 ...
- Verilog篇(四)时序模型
时序模型:仿真器的时间推进模型,它反映了推进仿真时间和调度事件的方式. 1)门级时序模型:适用于分析所有的连续赋值语句,过程连续赋值语句,门级原语,用户自定义原语. 特点:任意时刻,任意输入变化都将重 ...
- C/S模型之UDP协议
说明:利用UDP协议,创建一个服务器和一个客户端.两者间进行通信.由客户端进行输入内容,而服务器将接受的内容进行再一次返回,并显示在服务端. // UDP_Seversock.cpp : 定义控制台应 ...
- Java设计模式应用——备忘录模式
备忘录模式主要用于存档.游戏中我们打boss前总会存档,如果打boss失败,则读取存档,重新挑战boss. 可以看出来,备忘录模式一般包括如下数据结构 1. 存档文件:用于恢复备份场景的必要数据: 2 ...
- oracle用户名和密码到期后如何处理
原因:确定是由于Oracle11g中默认在default概要文件中设置了“PASSWORD_LIFE_TIME=180天”所导致. 影响: 1.密码过期后,业务进程连接数据库异常,影响业务使用. 2. ...
- 2018年星际争霸AI挑战赛–三星与FB获冠亚军,中科院自动化所夺得季军
雷锋网 AI 科技评论消息,2018 年 11 月 13-17 日,AAAI 人工智能与交互式数字娱乐大会 (AI for Interactive Digital Entertainment) 在阿尔 ...
- Hive学习之路 (五)DbVisualizer配置连接hive
一.安装DbVisualizer 下载地址http://www.dbvis.com/ 也可以从网上下载破解版程序,此处使用的版本是DbVisualizer 9.1.1 具体的安装步骤可以百度,或是修改 ...
- 简单的HTML5 canvas游戏工作原理
HTML5已经不是一个新名词.它看上去很cool,有很多feature,大多数人普遍看好它的发展.对于我来说,最感兴趣的是它的canvas标签,可以结合Javascript来绘制游戏画面. 我们可以在 ...
- 02: python3使用email和smtplib库发送邮件
1.1 发送qq邮箱 注:python代理登录qq邮箱发邮件,是需要更改自己qq邮箱设置的.在这里大家需要做两件事情:邮箱开启SMTP功能 .获得授权码 教程链接 1.给单个人发邮件 参考 from ...
- 2018-2019-1 20189218《Linux内核原理与分析》第六周作业
向menuOS中增加命令 修改menu目录下的test.c文件,增加自己的函数定义,并在修改main()函数,按照前面的menuconfig的写法写好自己的menuconfig. 我选择的是acces ...