算法笔记--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 # ...
随机推荐
- 008-centos服务管理
- 3:2 OGNL 简介
OGNL : (对象图导航语言) 从一个对象到另一个对象 OGNL来源于Xwork: OGNL的作用: OGNL在数据进出值栈的时候进行类型转换
- 《算法C语言实现》————三道题目
1.对于N = 10,100和1000,记录你的运行环境中分别运行一下程序所花费的时间.(用python) import datetime global a a = 0 def time_1(s): ...
- C++矩阵库 Eigen 快速入门
最近需要用 C++ 做一些数值计算,之前一直采用Matlab 混合编程的方式处理矩阵运算,非常麻烦,直到发现了 Eigen 库,简直相见恨晚,好用哭了. Eigen 是一个基于C++模板的线性代数库, ...
- Python: 字符串开头或结尾匹配str.startswith(),str.endswith()
问题 需要通过指定的文本模式去检查字符串的开头或者结尾,比如文件名后缀,URLScheme 等等. 解决方案 1.检查字符串开头或结尾的一个简单方法是使用str.startswith() 或者是str ...
- react脚手架构建工程
https://blog.csdn.net/qtfying/article/details/78665664 第二步:安装less包: https://segmentfault.com/a/11900 ...
- 神州行省内流量套餐6元500M申请,发送BLSN6到10086即可
神州行流量套餐,神州行省内流量套餐6元500M申请,发送BLSN6到10086即可申请开通专属流量包,比全国5元30M划算多了4G全国流量套餐 5元/30M 10元/100M 20元/300M 30元 ...
- Qt 学习之路 2(55):数据库操作
Qt 提供了 QtSql 模块来提供平台独立的基于 SQL 的数据库操作.这里我们所说的“平台独立”,既包括操作系统平台,又包括各个数据库平台.另外,我们强调了“基于 SQL”,因为 NoSQL 数据 ...
- hibernate的实现原理以及延迟加载
Hibernate是怎样实现呢?主要是依据反射机制. 现在以一次数据库查询操作分析Hibernate实现原理. 假设有一个用户表(tbl_user),表中字段有id,name,sex.同时有一个实体类 ...
- PowerDesigner 教程
摘自:http://www.cnblogs.com/advocate/p/3730027.html 目标:本文主要介绍PowerDesigner中概念数据模型 CDM的基本概念. 一.概念数据模型概述 ...