一、染色法,暴力染色,求解字典序最小解:
HDU1814
有n对人,每对有两个,有m对相互讨厌的关系,相互讨厌的两个人不能同时出现,每对人中出现且只能出现一个,判断能否成立,若成立,输出字典序最小的解。
2-sat水题:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=21000;
const int maxm=110000;
int head[maxn],ver[maxm],nt[maxm];
bool ha[maxn];
int st[maxn];
int tot=1,top=0;
void add(int x,int y)
{
ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}
bool dfs(int x)
{
if(ha[x^1]) return false;
if(ha[x]) return true;
ha[x]=true;
st[++top]=x;
for(int i=head[x];i;i=nt[i])
{
if(!dfs(ver[i]))
return false;
}
return true;
}
bool is_can(int n)
{
memset(ha,0,sizeof(ha));
for(int i=2;i<=n;i+=2)
{
if(ha[i]||ha[i^1]) continue;
top=0;
if(!dfs(i))
{
while(top) ha[st[top--]]=false;
if(!dfs(i^1)) return false;
}
}
return true;
}
int main(void)
{
int n,m;
int x,y;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,0,sizeof(head));
tot=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
x++,y++;
add(x,y^1);
add(y,x^1);
}
if(is_can(n*2+1))
{
for(int i=2;i<=2*n+1;i++)
{
if(ha[i])
printf("%d\n",i-1);
}
}
else printf("NIE\n");
}
return 0;
}
二、Priest John’s Busiest Day POJ - 3683 :
本题中,每场婚礼是一个变量,有开始时举行仪式和结束前举行仪式两种取值。
然后,就是一个2-sat问题了,本题不要求字典序最小的解,拓扑排序即可实现:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=2100;
const int maxm=4010000;
int head[maxn],ver[maxm],nt[maxm],dfn[maxn],low[maxn],c[maxn];
int head2[maxn],ver2[maxm],nt2[maxm],d[maxn],opp[maxn];
int st[maxn],s[maxn],t[maxn],dd[maxn],val[maxn];
bool ha[maxn];
int n,m,tot,tot2,top,cnt,sum;
void init(void)
{
memset(head,0,sizeof(head));
memset(head2,0,sizeof(head2));
memset(ha,0,sizeof(ha));
memset(d,0,sizeof(d));
memset(dfn,0,sizeof(dfn));
tot=tot2=1;
top=cnt=sum=0;
}
void add(int x,int y)
{
ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}
void add_c(int x,int y)
{
ver2[++tot2]=y,nt2[tot2]=head2[x],head2[x]=tot2;
}
bool is_can(int b1,int e1,int b2,int e2)
{
if(b1>=b2&&b1<e2||e1>b2&&e1<=e2||b1<=b2&&e1>=e2) return true;
if(b2>=b1&&b2<e1||e2>b1&&e2<=e1||b2<=b1&&e2>=e1) return true;
return false;
}
void tarjan(int x)
{
dfn[x]=low[x]=++sum;
st[++top]=x,ha[x]=true;
for(int i=head[x];i;i=nt[i])
{
if(!dfn[ver[i]])
{
tarjan(ver[i]);
low[x]=min(low[x],low[ver[i]]);
}
else if(ha[ver[i]])
low[x]=min(low[x],low[ver[i]]);
}
if(dfn[x]==low[x])
{
cnt++;
int y;
do
{
y=st[top--];
ha[y]=false;
c[y]=cnt;
}while(x!=y);
}
}
void tp(void)
{
memset(val,-1,sizeof(val));
for(int x=1;x<=n*2;x++)
for(int i=head[x];i;i=nt[i])
{
int y=ver[i];
if(c[x]==c[y]) continue;
add_c(c[y],c[x]);
d[c[x]]++;
}
queue<int>q;
for(int i=1;i<=cnt;i++)
{
if(!d[i]) q.push(i);
}
while(q.size())
{
int x=q.front();
q.pop();
if(val[x]==-1) val[x]=0,val[opp[x]]=1;
for(int i=head2[x];i;i=nt2[i])
{
d[ver2[i]]--;
if(d[ver2[i]]==0) q.push(ver2[i]);
}
}
return ;
}
int main(void)
{
while(scanf("%d",&n)!=EOF)
{
init();
int sh,sm,th,tm;
for(int i=1;i<=n;i++)
{
scanf("%d:%d%d:%d%d",&sh,&sm,&th,&tm,&dd[i]);
s[i]=sh*60+sm,t[i]=th*60+tm;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(is_can(s[i],s[i]+dd[i],s[j],s[j]+dd[j]))
add(i,n+j),add(j,n+i);
if(is_can(s[i],s[i]+dd[i],t[j]-dd[j],t[j]))
add(i,j),add(n+j,n+i);
if(is_can(t[i]-dd[i],t[i],s[j],s[j]+dd[j]))
add(n+i,n+j),add(j,i);
if(is_can(t[i]-dd[i],t[i],t[j]-dd[j],t[j]))
add(n+i,j),add(n+j,i);
}
}
for(int i=1;i<=2*n;i++)
{
if(!dfn[i]) tarjan(i);
}
bool flag=false;
for(int i=1;i<=n;i++)
{
if(c[i]==c[i+n])
{
flag=true;
printf("NO\n");
break;
}
opp[c[i]]=c[n+i],opp[c[n+i]]=c[i];
}
if(flag) continue;
printf("YES\n");
tp();
for(int i=1;i<=n;i++)
{
if(val[c[i]]==0)
{
printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+dd[i])/60,(s[i]+dd[i])%60);
}
else
{
printf("%02d:%02d %02d:%02d\n",(t[i]-dd[i])/60,(t[i]-dd[i])%60,t[i]/60,t[i]%60);
}
}
}
return 0;
}
还可以利用有向图求强连通分量的性质,直接获得拓扑序:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=2100;
const int maxm=4010000;
int head[maxn],ver[maxm],nt[maxm],dfn[maxn],low[maxn],c[maxn];
int d[maxn],opp[maxn];
int st[maxn],s[maxn],t[maxn],dd[maxn],val[maxn];
bool ha[maxn];
int n,m,tot,tot2,top,cnt,sum;
void init(void)
{
memset(head,0,sizeof(head));
memset(ha,0,sizeof(ha));
memset(d,0,sizeof(d));
memset(dfn,0,sizeof(dfn));
tot=tot2=1;
top=cnt=sum=0;
}
void add(int x,int y)
{
ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}
bool is_can(int b1,int e1,int b2,int e2)
{
if(b1>=b2&&b1<e2||e1>b2&&e1<=e2||b1<=b2&&e1>=e2) return true;
if(b2>=b1&&b2<e1||e2>b1&&e2<=e1||b2<=b1&&e2>=e1) return true;
return false;
}
void tarjan(int x)
{
dfn[x]=low[x]=++sum;
st[++top]=x,ha[x]=true;
for(int i=head[x];i;i=nt[i])
{
if(!dfn[ver[i]])
{
tarjan(ver[i]);
low[x]=min(low[x],low[ver[i]]);
}
else if(ha[ver[i]])
low[x]=min(low[x],low[ver[i]]);
}
if(dfn[x]==low[x])
{
cnt++;
int y;
do
{
y=st[top--];
ha[y]=false;
c[y]=cnt;
}while(x!=y);
}
}
int main(void)
{
while(scanf("%d",&n)!=EOF)
{
init();
int sh,sm,th,tm;
for(int i=1;i<=n;i++)
{
scanf("%d:%d%d:%d%d",&sh,&sm,&th,&tm,&dd[i]);
s[i]=sh*60+sm,t[i]=th*60+tm;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(is_can(s[i],s[i]+dd[i],s[j],s[j]+dd[j]))
add(i,n+j),add(j,n+i);
if(is_can(s[i],s[i]+dd[i],t[j]-dd[j],t[j]))
add(i,j),add(n+j,n+i);
if(is_can(t[i]-dd[i],t[i],s[j],s[j]+dd[j]))
add(n+i,n+j),add(j,i);
if(is_can(t[i]-dd[i],t[i],t[j]-dd[j],t[j]))
add(n+i,j),add(n+j,i);
}
}
for(int i=1;i<=2*n;i++)
{
if(!dfn[i]) tarjan(i);
}
bool flag=false;
for(int i=1;i<=n;i++)
{
if(c[i]==c[i+n])
{
flag=true;
printf("NO\n");
break;
}
opp[i]=n+i,opp[n+i]=i;
}
if(flag) continue;
printf("YES\n");
for(int i=1;i<=n*2;i++)
{
val[i]=c[i]>c[opp[i]];
}
for(int i=1;i<=n;i++)
{
if(val[i]==0)
{
printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+dd[i])/60,(s[i]+dd[i])%60);
}
else
{
printf("%02d:%02d %02d:%02d\n",(t[i]-dd[i])/60,(t[i]-dd[i])%60,t[i]/60,t[i]%60);
}
}
}
return 0;
}