先续0x03的Tarjan
HDU 3836 Equivalent Sets 给有向图,至少添加多少边才成为强连通图?
思路:tarjan缩点。
对于强连通缩点图中的每一个点,一定至少有一个出度和一个入度。
我们就求缩点图中有多少个点的入度为0,多少个点的出度为0.
答案就是max(a1,a2)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20004;
int low[maxn],num[maxn],dfn,cnt,sccno[maxn],in[maxn],out[maxn];
stack<int>s;
vector<int>G[maxn];
void dfs(int u){
s.push(u);
num[u]=low[u]=++dfn;
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[v],low[u]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(low[u]==num[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
if(u==v) break;
}
}
}
void Tarjan(int n){
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
dfn=cnt=0;
for(int i=1;i<=n;++i)
if(!num[i]) dfs(i);
for(int i=1;i<=n;++i){
for(int j=0;j<G[i].size();++j){
int v=G[i][j];
if(sccno[i]==sccno[v]) continue;
in[sccno[v]]++;out[sccno[i]]++;
}
}
int a1=0,a2=0;
for(int i=1;i<=cnt;++i) {
if(in[i]==0) a1++;
if(out[i]==0) a2++;
}
if(cnt==1) cout<<0<<endl;
else cout<<max(a1,a2)<<endl;
}
// max(入度=0的缩点个数,出度=0的缩点个数)
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;++i) G[i].clear();
for(int i=1;i<=m;++i){
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
}
Tarjan(n);
}
}
HDU 3639 Hawk-and-Chicken 投票具有传递性 求最大得票
题意:一个投票,规则:投票具有传递性,A支持B,B支持C,那么C获得2票(A.B共两票),输出最多能获得的票数是多少张和获得最多票数的人是谁?(如果有多个人获得的票数都是最多的,就将他们全部输出)。
思路:tarjan缩点。缩点图每个点内部得票数一样。缩点图只有出度为0的点有可能得票最多。统计出度为0的点的得票数。如何统计?建反图,出度为0的点的得票即反图入度为0的点进行MST的size。
#include<bits/stdc++.h>
using namespace std;
/*
* 建反图。求sz。
*/
const int maxn = 5003;
int low[maxn],num[maxn],dfn,cnt,mx,sccno[maxn],in[maxn],id,sccn[maxn];
vector<int>G[maxn];
vector<int>G2[maxn]; // 反图
stack<int>s;
void dfs(int u){
s.push(u);
num[u]=low[u]=++dfn;
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(num[u]==low[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
sccn[cnt]++;
if(u==v) break;
}
}
}
int sup[maxn],vis[maxn],sm;
void dfs2(int u){
vis[u]=1;
sm+=sccn[u];
for(int i=0;i<G2[u].size();++i){
int v=G2[u][i];
if(!vis[v]) dfs2(v);
}
}
void init(int n){
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
memset(in,0,sizeof(in));
memset(sccn,0,sizeof(sccn));
memset(sup,0,sizeof(sup));
for(int i=1;i<=n;++i) G2[i].clear();
for(int i=1;i<=n;++i) G[i].clear();
dfn=cnt=mx=0;
}
void Tarjan(int n){
for(int i=1;i<=n;++i) if(!num[i]) dfs(i);
for(int i=1;i<=n;++i){
for(int j=0;j<G[i].size();++j){
int v=G[i][j];
if(sccno[i]==sccno[v]) continue;
G2[sccno[v]].push_back(sccno[i]);
in[sccno[i]]++;
}
}
for(int i=1;i<=cnt;++i) if(!in[i]) {
sm=0;
memset(vis,0,sizeof(vis));
dfs2(i);
sup[i]=sm-1;
mx=max(mx,sup[i]);
}
cout<<"Case "<<++id<<": "<<mx<<endl;
int isf=1;
for(int i=1;i<=n;++i)
if(sup[sccno[i]]==mx) {
if(isf) cout<<i-1,isf=0;
else cout<<" "<<i-1;
}
putchar(10);
}
int main(){
int t;scanf("%d",&t);
while(t--){
int n,m;scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;++i){
int u,v;scanf("%d%d",&u,&v);
G[++u].push_back(++v);
}
Tarjan(n);
}
}
HDU 1530 Maximum Clique 最大团简单题 dfs爆搜
#include<bits/stdc++.h>
using namespace std;
const int maxn = 53;
int mp[maxn][maxn];
int mx,n;
int point[maxn];
bool isclique(int num,int p){
for(int i=1;i<=num;++i)
if(!mp[point[i]][p])
return 0;
return 1;
}
void dfs(int num,int p){
for(int i=p;i<=n;++i){
if(isclique(num,i)){
point[num+1]=i;
dfs(num+1,i+1);
}
}
mx=max(mx,num);
}
int main(){
while(~scanf("%d",&n),n){
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&mp[i][j]);
mx=0;
dfs(0,1);
cout<<mx<<endl;
}
}
2-SAT
非常详细的讲解博客!
HDU 3062 Party 2sat入门题
给出不能同时出现的关系。
判断是否可行。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1003;
vector<int>G[maxn<<1]; // 原图
stack<int>s;
int low[maxn<<1],num[maxn<<1],sccno[maxn<<1],dfn,cnt;
void dfs(int u){
low[u]=num[u]=++dfn;
s.push(u);
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(low[u]==num[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
if(u==v) break;
}
}
}
bool Tarjan(int n){
for(int i=0;i<n;++i)
if(!num[i]) dfs(i);
for(int i=0;i<n-1;i+=2)
if(sccno[i]==sccno[i+1]) return 0;
return 1;
}
void init(int n){
for(int i=0;i<n;++i) G[i].clear();
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
dfn=cnt=0;
}
int main(){
int n,m;while(~scanf("%d%d",&n,&m)){
init(2*n);
for(int i=1;i<=m;++i){
int f1,f2,u,v;scanf("%d%d%d%d",&u,&v,&f1,&f2);
G[u<<1|f1].push_back(v<<1|(f2^1));
G[v<<1|f2].push_back(u<<1|(f1^1));
}
if(Tarjan(2*n)) puts("YES");
else puts("NO");
}
}
HDU 1824 Let’s go home sat简单题
这种题居然一发过了。。好神奇
- 考虑一对1与0:即队长留下是1,队友留下是0.
- 建图。
- 用一个map储存编号的对应关系。
- 非此即彼:A->B’, B->A’.
- 当非此即彼的是同一个队伍的队友,必选队长。队友到队长连一条边。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3004;
int b[maxn];
vector<int>G[maxn];
stack<int>s;
int low[maxn],num[maxn],cnt,sccno[maxn],dfn;
void dfs(int u){
num[u]=low[u]=++dfn;
s.push(u);
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(low[u]==num[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
if(u==v) break;
}
}
}
void Tarjan(int n){
for(int i=0;i<n;++i) if(!num[i]) dfs(i);
}
void init(int n){
for(int i=0;i<n;++i) G[i].clear();
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
dfn=cnt=0;
}
int main(){
int t,m;
while(~scanf("%d%d",&t,&m)){
int cnt2=0;
init(2*t);
for(int i=1;i<=t;++i){
int u,v,x;scanf("%d%d%d",&u,&v,&x);
b[u]=cnt2++; // 偶数点:队长
b[v]=cnt2,b[x]=cnt2++; // 单数点:队员
}
int flg=0;
for(int i=1;i<=m;++i){
int u,v,ru,rv;scanf("%d%d",&u,&v);
u=b[u],v=b[v],ru=(u&1)?u-1:u+1,rv=(v&1)?v-1:v+1;
if(u==v){G[u].push_back(u-1);continue;}
G[u].push_back(rv);
G[v].push_back(ru);
}
Tarjan(2*t);
for(int i=0;i<2*t;i+=2){
if(sccno[i]==sccno[i+1]) {flg=1;break;}
}
if(flg) cout<<"no"<<endl;
else cout<<"yes"<<endl;
}
}
HDU 4115 Eliminate the Conflict 2-SAT
这样考虑:
SAT问题都是解决1/0的问题的。
正好,每轮Alice也只能在两个选项中选一个。那么其一算0,其二算1.
两轮不能选一样的:那么当一样的时候,选另一个。
两轮一定要选一样的:当不一样的时候,选另一个。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20003;
int a[maxn];
vector<int>G[maxn];
stack<int>s;
int num[maxn],low[maxn],dfn,cnt,sccno[maxn];
int b[maxn]; // 用来储存每个点分别是哪一号人物
void dfs(int u){
num[u]=low[u]=++dfn;
s.push(u);
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(num[u]==low[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
if(u==v) break;
}
}
}
void Tarjan(int n){
for(int i=0;i<n;++i) if(!num[i]) dfs(i);
}
void init(int n){
for(int i=0;i<=n;++i) G[i].clear();
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
dfn=cnt=0;
}
int main(){
int t,cas=0;cin>>t;
while(t--){
int n,m;cin>>n>>m;
init(n<<1);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
if(a[i]==1) b[2*i]=2,b[2*i+1]=3;
if(a[i]==2) b[2*i]=1,b[2*i+1]=3;
if(a[i]==3) b[2*i]=1,b[2*i+1]=2;
}
for(int i=1;i<=m;++i){
int u,v,opt;scanf("%d%d%d",&u,&v,&opt);
u--,v--;
if(opt){
// 要两者不同。只需要让两者相同的那两个转向对方另一个。
for(int j=2*u;j<=2*u+1;++j){
for(int k=2*v;k<=2*v+1;++k){
if(b[j]==b[k]){
G[j].push_back(k^1);
G[k].push_back(j^1);
}
}
}
}else{
// 要两者相同,那么不同的时候转向另一个
for(int j=2*u;j<=2*u+1;++j){
for(int k=2*v;k<=2*v+1;++k){
if(b[j]!=b[k]){
G[j].push_back(k^1);
G[k].push_back(j^1);
}
}
}
}
}
Tarjan(n<<1);
int flg=0;
for(int i=0;i<(n<<1);i+=2){
if(sccno[i]==sccno[i+1]) {flg=1;break;}
}
cout<<"Case #"<<++cas<<": ";
if(flg) cout<<"no"<<endl;
else cout<<"yes"<<endl;
}
}
HDU 4421 Bit Magic
题意:
a[n]是由b[n][n]经过以上函数得到,现给出b,问是否有可行a。
一开始的做法把每个Bit都建了一个点,然后MLE了(。
现在改为对每个bit都进行一次2-sat。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 503*2;
vector<int>G[maxn];
int num[maxn],low[maxn],sccno[maxn],dfn,cnt;
stack<int>s;
void init(int n){
for(int i=0;i<n;++i) G[i].clear();
memset(num,0,sizeof(num));
memset(sccno,0,sizeof(sccno));
dfn=cnt=0;
}
void dfs(int u){
num[u]=low[u]=++dfn;
s.push(u);
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!num[v]){
dfs(v);
low[u]=min(low[v],low[u]);
}else if(!sccno[v])
low[u]=min(low[u],num[v]);
}
if(num[u]==low[u]){
cnt++;
while(1){
int v=s.top();s.pop();
sccno[v]=cnt;
if(u==v) break;
}
}
}
void Tarjan(int n){
for(int i=0;i<n;++i) if(!num[i]) dfs(i);
}
int b[maxn][maxn];
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;++i) for(int j=0;j<n;++j)
scanf("%d",&b[i][j]);
int flg=1;
for(int i=0;i<n&&flg;++i){
if(b[i][i]!=0) flg=0;
for(int j=i+1;j<n&&flg;++j)
if(b[i][j]!=b[j][i]) flg=0;
}
if(!flg){cout<<"NO"<<endl;continue;}
for(int k=0;k<31&&flg;++k){
init(n<<1);
for(int i=0;i<n;++i){
for(int j=i+1;j<n;++j){
int is1=b[i][j]&(1<<k);
int u=2*i,v=2*j;
if((i%2)&&(j%2)){
if(is1){
G[u].push_back(v^1);
G[v].push_back(u^1);
}else{
G[u^1].push_back(u);
G[v^1].push_back(v);
}
}else if(!(i%2)&&!(j%2)){
if(is1){
G[u].push_back(u^1);
G[v].push_back(v^1);
}else{
G[u^1].push_back(v);
G[v^1].push_back(u);
}
}else{
if(is1){
G[u].push_back(v^1);
G[v].push_back(u^1);
G[u^1].push_back(v);
G[v^1].push_back(u);
}else{
G[u^1].push_back(v^1);
G[v^1].push_back(u^1);
G[u].push_back(v);
G[v].push_back(u);
}
}
}
}
Tarjan(n<<1);
for(int i=0;i<(n<<1);i+=2){
if(sccno[i]==sccno[i+1]) {flg=0;break;}
}
}
if(!flg) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}