海亮第二天,想着说预习一下的,可是昨天10h+都没有把那六道题打完,就打了四道,上午就讲完课了,还是延续昨天的博客,今天接着总结。。
欧拉路
开头就是特别经典的七桥问题,讲欧拉路一定会讲到的问题,也就是从小最熟悉的一笔画问题。
欧拉路:如果一个图存在一笔画,则一笔画的路径就是欧拉路。
欧拉回路:如果一个图存在一笔画而且最后又回到了起点(不重复走过所有的路径),则其路径就是欧拉回路。
存在欧拉路的条件:图是连通的,有且只有2 个奇点。
存在欧拉回路的条件:图是连通的,有0 个奇点。
延伸:
哈密尔顿环: 指不重复走过所有点并且最后还能回到起点的回路。
Hierholzer
(具体怎么念,还没不是很清楚,好像是海尔后泽…(xia shou de bie xin ))
伪代码:
(因为不会很好的总结,就直接上大神的PPT>_<)
步骤:
1 判断奇点数。奇点数若为0则任意指定起点,奇点数若为2则指定起点为奇点。
2 开始递归函数Hierholzer(x):
循环寻找与x相连的边(x,u):
删除(x,u)
删除(u,x)
Hierholzer(u);
将x插入答案队列之中
具体代码:
void euler(int u)
{
for(int i=begin;i<=end;i++)
if(a[u][i]>=1)\
{
a[u][i]--;
a[i][u]--;
euler(i);
}
ans[++anssize]=u;
}
放一个欧拉回路的题:POJ - 1386
是有意思的个板子 题,,,
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int fa[26],r[26],c[26],v[26];
char a[1200];
//并查集
int find(int n) {return n==fa[n]?n:fa[n]=find(fa[n]);}
void merge(int a,int b) {a=find(a);b=find(b);fa[a]=b;}
int main()
{
int n;
cin>>n;
while(n--)
{
int m;
cin>>m;
memset(v,0,sizeof(v));
memset(c,0,sizeof(c));
memset(r,0,sizeof(r));
for(int i=0;i<26;i++) fa[i]=i;
for(int i=0;i<m;i++)
{
cin>>a;
int x=a[0]-'a';
int y=a[strlen(a)-1]-'a';//只用记录这串字母的首尾即可,可以简化操作并降低复杂度
merge(x,y);
v[x]=v[y]=true;
c[x]++; r[y]++;//跑欧拉路
}
int q1=0,q2=0,q3=0,q4=0;
for(int i=0;i<26;i++)
if(v[i])
{
q1++;
if(fa[i]==i)q2++;
if(c[i]==r[i])q3++;
if(abs(r[i]-c[i])==1)q4++;
}
if(q2==1&&(q1==q3)||(q4==2&&q3+q4==q1))
//判断是不是环或是可以连接
cout<<"Ordering is possible."<<endl;
else cout<<"The door cannot be opened."<<endl;
}
return 0;
}
在放个有意思的题…
太鼓达人 HYSBZ - 3033
这道题好有意思好有意思好有意思 ,
大佬给的题解就一句话:欧拉回路
本蒟蒻的题解就算了吧,(一定会补齐的,要相信我!!!)
#include<iostream>
using namespace std;
int ans,kk,n,t,d[1000000];
bool v[100000];
bool euler(int x,int y)
{
if(v[x]) return 0;
d[y]=x&1;
v[x]=true;
if(y==kk) return 1;
if(euler((x<<1)&t,y+1)) return 1;
if(euler((x<<1|1)&t,y+1)) return 1;
v[x]=false;
}
int main()
{
cin>>n;
kk=1<<n;
cout<<kk<<' ';
t=kk-1;
euler(kk-2,1);
for(int i=1;i<=kk;i++)
cout<<d[i];
return 0;
}
//是不是有点像hzwer的代码,,,原因就不说了,跟没有题解一个原因,,,
(早知道不会就不放了,,,>_<)
拓扑排序
topu板子
void topu()
{
int i,j,now;
queue<int>q;
for(int i=1;i<=n;i++)
if(!ind[i]) q.push(i);
while(!q.empty())
{
now=q.front;q.pop();
ans[++cnt]=now;
for(int i=last[now];i;i=e[i].next)
{
ind[e[i].ver]--;
if(!ind[e[i].ver])q.push(e[i].ver);
}
if(cnt!=n) puts("-1");
else for(int i=1;i<=n;i++)
// cout<<ans[i]<<' ';
printf("%d",ans[i]);
// 一个极好的建议:别用cout,在1e6之后就死了,曾经本蒟蒻就是没有这个常识,在一道题中整整TLE了两个小时,还TLE了整整10遍,,一把辛酸泪
}
}
放一个也很有意思的题,,,(不是很想这么说了>_>)
CodeForces - 516B
一道很有意思的topu+搜索的题,这个应该算有意思了,
#include<bits/stdc++.h>
#define ll long long
#define sea 0x3fffffff
#define mp make_pair
using namespace std;
int n,m;
int d[2500][2500];
int dx[]={0,1,-1,0,0};
int dy[]={0,0,0,-1,1};
char od[2200][22000]; //读入时的数组
queue<pair<int,int> >Q;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%s",od[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(od[i][j]=='*') d[i][j]=0;
else
for(int k=1;k<=4;k++)
{
int xx=dx[k]+i,yy=dy[k]+j;
if(xx<1||xx>n||yy<1||yy>m||od[xx][yy]=='*') continue;
d[i][j]++;
}
}//以上是读入并预处理
//下面开始大暴搜:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(d[i][j]==1) Q.push(mp(i,j));
while(!Q.empty())
{
int x=Q.front().first,y=Q.front().second; Q.pop();
if(d[x][y]==0) continue;
for(int i=1;i<=4;i++)
{
int xx=dx[i]+x,yy=dy[i]+y;
if(xx<1||xx>n||yy<1||yy>m||od[xx][yy]!='.') continue;
d[x][y]=d[xx][yy]=0;
if(i==1) od[x][y]='^',od[xx][yy]='v';
if(i==2) od[x][y]='v',od[xx][yy]='^';
if(i==3) od[x][y]='>',od[xx][yy]='<';
if(i==4) od[x][y]='<',od[xx][yy]='>';
for(int j=1;j<=4;j++)
{
int xxx=dx[j]+xx,yyy=dy[j]+yy;
if(xxx<1||xxx>n||yyy<1||yyy>m||od[xxx][yyy]!='.') continue;
if(--d[xxx][yyy]==1) Q.push(mp(xxx,yyy));
}
}
}
//要是会搜索就特别简单了,要是不会的话就不是会很容易了O.O
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(od[i][j]=='.')
{cout<<"Not unique"<<endl; return 0;}
for(int i=1;i<=n;i++) cout<<od[i]+1<<endl;
return 0;
}
差分约束
(粘概念):通俗一点地说,差分约束系统就是一些不等式的组,而我们的目标是通过给定的约束不等式组求出最大值或者最小值或者差分约束系统是否有解。
之所以差分约束系统可以通过图论的最短路来解,是因为
x
j
±
x_j\pm
xj±
x
i
x_i
xi
<
=
b
k
<= bk
<=bk类似最短路中的三角不等式
d
[
v
]
<
=
d
[
u
]
+
w
[
u
]
[
v
]
d[v]<=d[u]+w[u][v]
d[v]<=d[u]+w[u][v],
即
d
[
v
]
−
d
[
u
]
<
=
w
[
u
]
[
v
]
d[v]-d[u]<=w[u][v]
d[v]−d[u]<=w[u][v]。而求取最大值的过程类似于最短路算法中的松弛过程。
三角不等式:
B
−
A
<
=
c
B-A<= c
B−A<=c
C
−
B
<
=
a
C-B<=a
C−B<=a
C
−
A
<
=
b
C-A<=b
C−A<=b
如果要求
C
−
A
C-A
C−A的最大值,可以知道为
m
i
n
(
b
,
a
+
c
)
min(b, a + c)
min(b,a+c),而这正对应了C到A的最短路。
这个知识点需要用题巩固,毕竟这只是一种思想。
POJ - 3169
#include<bits/stdc++.h>
#define sea 11000000
#define maxn 85251
using namespace std;
int tot,n,ml,md;
int d[110000],sum[110000];
bool v[110000];
struct see {int next,last,ver,edge;}e[110000];
void add(int x,int y,int z)
{e[++tot].ver=y;e[tot].edge=z;e[tot].next=e[x].last;e[x].last=tot;}
int spfa()
{
queue<int>Q;
for(int i=2;i<=maxn;i++)
d[i]=sea;
sum[1]++; d[1]=0; v[1]=1; Q.push(1);
while(!Q.empty())
{
int x=Q.front(); Q.pop(); v[x]=0;
for(int i=e[x].last;i;i=e[i].next)
{
int y=e[i].ver,z=e[i].edge;
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
if(!v[y])
{
v[y]=1;Q.push(y);
sum[y]++;
if(sum[y]>n) return 1;
}
}
}
}
return 0;
}
int main()
{
cin>>n>>ml>>md;
while(ml--){int x,y,z; cin>>x>>y>>z;add(x,y,z);}
while(md--){int x,y,z; cin>>x>>y>>z;add(y,x,-z);}
//差分约束就是在建图时的一种思想
for(int i=1;i<n;i++)
add(i+1,i,0);
if(spfa()) cout<<-1<<endl;//跑个裸的spfa即可
else if(d[n]==sea) cout<<-2<<endl;
else cout<<d[n]<<endl;
return 0;
}
放一道有意思 的题:
POJ - 2983
dist[A]-dist[B]>=X,表示A在B北边至少X光年位置。变形为:dist[B]<=dist[A]-X;所以A指向B有条权值为-X的边。对于A-B = X,建两条边dist[B]<=dist[A]-X,dist[A]<=dist[B]+X。
SPFA存在负权环则说明不符合实际情况。
#include<bits/stdc++.h>
#define maxn 95276
#define sea 100000
using namespace std;
int tot,n,m,d[1010],v[1010],sum[1010],last[1010];
struct see{int ver,edge,next;}e[1000000];
void add(int x,int y,int z)
{ e[++tot].ver=y;e[tot].edge=z;e[tot].next=last[x];last[x]=tot;}
int spfa()
{
queue<int>Q;
memset(sum,0,sizeof(sum));
memset(v,0,sizeof(v));
memset(d,0x3f,sizeof(d));
d[0]=0; v[0]=1; Q.push(0);sum[0]=1;
while(!Q.empty())
{
int x=Q.front(); Q.pop(); v[x]=0; sum[x]++;
if(sum[x]>n) return 0;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].ver,z=e[i].edge;
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
if(!v[y]) v[y]=1,Q.push(y);
}
}
}
return 1;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(last,0,sizeof(last));
char c[2];
int x,y,z;
while(m--)
{
scanf("%s",c);
if(c[0]=='P')
{
cin>>x>>y>>z;
add(y,x,z),add(x,y,-z);
}
else
{
cin>>x>>y;
add(x,y,-1);
}
}
for(int i=1;i<=n;i++)
add(0,i,0);//与超级汇点类似的一个大点
if(spfa())cout<<"Reliable"<<endl;
else cout<<"Unreliable"<<endl;
}
return 0;
}
再放一道,,的题(不说了),
HDU - 3440
令d[i]表示第i栋房子与第一栋房子之间的最大距离,那么我们要求的就是的d[n]。首先每栋房子之间的相对位置已经确定且不能在同一位置,那么d[i+1] > d[i],即d[i+1]- d[i]>=1,即d[i]-d[i+1] <=-1。
还有一个条件是相邻两个要跳跃的距离不可以大于D,可以写出另一个方程。
有一点小问题就是建边的方向和跳跃的方向不同,我们要保证是向一个方向建图的。
若从左到右建边,如果1在n的左面就从1到n跑SPFA,反之则从n到1跑最短路了。
#include<bits/stdc++.h>
using namespace std;
bool v[11000];
int tot,n,m,T,d[11000],last[11000],sum[11000];
struct see{int num,v;}g[1000000];
bool compare(see a,see b) {return a.v<b.v;}
struct watch{int ver,edge,next;}e[1000000];
void add(int x,int y,int z)
{ e[++tot].ver=y;e[tot].edge=z;e[tot].next=last[x];last[x]=tot;}
int spfa(int u,int s)
{
queue<int>Q;
memset(sum,0,sizeof(sum));
memset(v,0,sizeof(v));
memset(d,0x3f,sizeof(d));
d[u]=0; v[u]=1; Q.push(u);
while(!Q.empty())
{
int x=Q.front(); Q.pop(); v[x]=0; sum[x]++;
if(sum[x]>n) return -1;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].ver,z=e[i].edge;
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
if(!v[y]) v[y]=1,Q.push(y);
}
}
}
return d[s];
}
int main()
{
cin>>T;
for(int t=1;t<=T;t++)
{
memset(last,0,sizeof(last));
tot=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>g[i].v;
g[i].num=i;
if(i!=n) add(i+1,i,-1);
}
sort(g+1,g+n+1,compare);
for(int i=1;i<n;i++)
{
int x=g[i].num,y=g[i+1].num;
if(x>y) swap(x,y);
add(x,y,m);
}
int x=g[1].num,y=g[n].num;
if(x>y) swap(x,y);
cout<<"Case "<<t<<": "<<spfa(x,y)<<endl;
}
return 0;
}
今天就总结到这里吧,这个专题也不是算太难,只要好好学学就能大概读懂思路,然后就是刷题,其实最好的老师就是代码。(本人不对自己的话负法律责认>v<).