本文来自《挑战程序设计竞赛》4.3 成为图论大师之路
1.强连通分量分解
1.定义
2.解法原理
3.代码
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
//模板
const int MAX_V=1005;
int V;//顶点数
vector<int> G[MAX_V];//图的邻接表表示
vector<int> rG[MAX_V];//把边反向后的图
vector<int> vs;//后序遍历顺序的顶点列表
bool used[MAX_V];//访问标记
int cmp[MAX_V];//所属强连通分量的拓扑序
void add_edge(int from,int to)
{
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v)
{
used[v]=true;
for(int i=0;i<G[v].size();i++){
if(!used[G[v][i]]){
dfs(G[v][i]);
}
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k;
for(int i=0;i<rG[v].size();i++){
if(!used[rG[v][i]]){
rdfs(rG[v][i],k);
}
}
}
int scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int v=0;v<V;v++){
if(!used[v]){
dfs(v);
}
}
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!used[vs[i]]){
rdfs(vs[i],k++);
}
}
return k;
}
//模板
int main()
{
cin>>V;
int a,b;
cin>>a>>b;
while(a>=0&&b>=0){
add_edge(a,b);
cin>>a>>b;
}
cout<<scc()<<endl;
return 0;
}
2.2-SAT
1.定义
2.代码
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
//模板
const int MAX_V=1005;
int V;//顶点数
vector<int> G[MAX_V];//图的邻接表表示
vector<int> rG[MAX_V];//把边反向后的图
vector<int> vs;//后序遍历顺序的顶点列表
bool used[MAX_V];//访问标记
int cmp[MAX_V];//所属强连通分量的拓扑序
void add_edge(int from,int to)
{
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v)
{
used[v]=true;
for(int i=0;i<G[v].size();i++){
if(!used[G[v][i]]){
dfs(G[v][i]);
}
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k;
for(int i=0;i<rG[v].size();i++){
if(!used[rG[v][i]]){
rdfs(rG[v][i],k);
}
}
}
int scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int v=0;v<V;v++){
if(!used[v]){
dfs(v);
}
}
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!used[vs[i]]){
rdfs(vs[i],k++);
}
}
return k;
}
//模板
int main()
{
//布尔公式(aV~b)^(bVc)^(~cV~a)
//构造6个顶点a,b,c,~a,~b,~c
V=6;
//aV~b
add_edge(3,4);
add_edge(1,0);
//bVc
add_edge(4,2);
add_edge(5,1);
//~cV~a
add_edge(2,3);
add_edge(0,5);
//进行强连通分量分解
scc();
for(int i=0;i<3;i++){
if(cmp[i]==cmp[i+3]){
cout<<"No"<<endl;
return 0;
}
}
//如果可满足,输出一组解
cout<<"Yes"<<endl;
for(int i=0;i<3;i++){
if(cmp[i]>cmp[i+3]){
cout<<"true"<<endl;
}
else{
cout<<"false"<<endl;
}
}
return 0;
}
3.Priest John's Busiest Day(Poj 3683)
1.题目原文
Priest John's Busiest Day
Description John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti - Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings. Note that John can not be present at two weddings simultaneously. Input The first line contains a integer N ( 1 ≤ N ≤ 1000). Output The first line of output contains "YES" or "NO" indicating whether John can be present at every special ceremony. If it is "YES", output another N lines describing the staring time and finishing time of all the ceremonies. Sample Input 2 08:00 09:00 30 08:15 09:00 20 Sample Output YES 08:00 08:30 08:40 09:00 Source
POJ Founder Monthly Contest – 2008.08.31, Dagger and Facer
|
2.解题思路
3.AC代码
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
//模板
const int MAX_N=1005;
int N;
int S[MAX_N],T[MAX_N],D[MAX_N];
const int MAX_V=2005;
int V;//顶点数
vector<int> G[MAX_V];//图的邻接表表示
vector<int> rG[MAX_V];//把边反向后的图
vector<int> vs;//后序遍历顺序的顶点列表
bool used[MAX_V];//访问标记
int cmp[MAX_V];//所属强连通分量的拓扑序
void add_edge(int from,int to)
{
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v)
{
used[v]=true;
for(int i=0;i<G[v].size();i++){
if(!used[G[v][i]]){
dfs(G[v][i]);
}
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
used[v]=true;
cmp[v]=k;
for(int i=0;i<rG[v].size();i++){
if(!used[rG[v][i]]){
rdfs(rG[v][i],k);
}
}
}
void scc()
{
memset(used,0,sizeof(used));
vs.clear();
for(int v=0;v<V;v++){
if(!used[v]){
dfs(v);
}
}
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!used[vs[i]]){
rdfs(vs[i],k++);
}
}
return;// k;
}
//模板
void solve()
{
//0-N-1:x[i]
//N-2N-1:~x[i]
V=2*N;
for(int i=0;i<N;i++){
for(int j=0;j<i;j++){
if(min(S[i]+D[i],S[j]+D[j])>max(S[i],S[j])){
//x[i]--~x[j],x[j]--~x[i]
add_edge(i,N+j);
add_edge(j,N+i);
}
if(min(S[i]+D[i],T[j])>max(S[i],T[j]-D[j])){
//x[i]--x[j],~x[j]--~x[i]
add_edge(i,j);
add_edge(N+j,N+i);
}
if(min(T[i],S[j]+D[j])>max(S[j],T[i]-D[i])){
//~x[i]--~x[j],x[j]--x[i]
add_edge(N+i,N+j);
add_edge(j,i);
}
if(min(T[i],T[j])>max(T[i]-D[i],T[j]-D[j])){
//~x[i]--x[j],~x[j]--x[i]
add_edge(N+i,j);
add_edge(N+j,i);
}
}
}
scc();
//判断是否可满足
for(int i=0;i<N;i++){
if(cmp[i]==cmp[N+i]){
printf("NO\n");
return;
}
}
//找到一组解
printf("YES\n");
for(int i=0;i<N;i++){
if(cmp[i]>cmp[N+i]){
//x[i]为真,在结婚仪式开始时
printf("%02d:%02d %02d:%02d\n",S[i]/60,S[i]%60,(S[i]+D[i])/60,(S[i]+D[i])%60);
}
else{
//x[i]为假,在结婚仪式结束时
printf("%02d:%02d %02d:%02d\n",(T[i]-D[i])/60,(T[i]-D[i])%60,T[i]/60,T[i]%60);
}
}
}
int main()
{
cin>>N;
for(int i=0;i<N;i++){
char s1[10],s2[10];
cin>>s1>>s2;
int a=10*(s1[0]-'0')+(s1[1]-'0');
int b=10*(s1[3]-'0')+(s1[4]-'0');
int c=10*(s2[0]-'0')+(s2[1]-'0');
int d=10*(s2[3]-'0')+(s2[4]-'0');
S[i]=60*a+b;
T[i]=60*c+d;
cin>>D[i];
}
solve();
return 0;
}
3.LCA
1.定义
2.基础二分搜索的算法
vector<int> G[maxv];//邻接表表示
int root;//根节点
int parent[maxv];//父亲节点(根节点的父亲节点为-1)
int depth[maxv];//节点的深度
void dfs(int v,int p,int d)
{
parent[v]=p;
depth[v]=d;
for(int i=0;i<G[v].size();i++){
if(G[v][i]!=p){
dfs(G[v][i],v,d+1);
}
}
}
//预处理
void init()
{
//预处理出parent和depth
dfs(root,-1,0);
}
//计算u和v的LCA
int lca(int u,int v)
{
//让u和v走到同一深度
while(depth[u]>depth[v]){
u=parent[u];
}
while(depth[v]>depth[u]){
v=parent[v];
}
//让u和v走到同一节点
while(u!=v){
u=parent[u];
v=parent[v];
}
return u;
}