最近练题+图着色+欧拉通路

hdu 3499
两边双向最短路,枚举中间边即可。
注意输入的城市名字会有数字,所以很难用Streamtokenizer,还是老老实实的用Scanner吧。。。

hdu 3501
经典的欧拉函数。经过推倒,我们可以得知最后的答案是( n - 1 - E(n) ) / 2
来一个欧拉函数的模板:
static long Euler(long n) {
long ans = n;
if (n % 2 == 0) {
while (n % 2 == 0)
n /= 2;
ans /= 2;
}
for (int i = 3; i * i <= n; i += 2)
if (n % i == 0) {
while (n % i == 0)
n /= i;
ans = ans * (i - 1) / i;
}
if (n > 1)
ans -= ans / n;
return ans;
}

hdu 1269
裸的强连通分量

poj 1659
Haval_Hakimi定理,这个大家网上查找就行。

poj 3031
原来要全部变成long,包括node和edge才行。。。一开始怕MLE,结果就WA了。。。囧。。。

poj 2449 : k短路
出题者的算法是首先计算任意一点到终点的最短距离,使用这个距离作为A*算法中H函数(从当前点到终点的估计函数)使用A*算法。跟一般的A*算法不同的是,一个结点可以被多次扩展到(

A*算法中只保留到达一个结点的最短路径)。使用这个算法就可以得到起点到终点的最短路径,第二短路径,第三短路径……

着色问题:
poj 2284
自己第一道比较复杂的计算几何。
题目思路很简单,求出顶点数、边数,最后利用欧拉定理就能直接解决问题了,而且这道题目不需要非常复杂的公式。
提几个在打的时候出现的问题:
对于直线的一般方程式:ax+by+c=0
如果我们遇到了两个顶点(x1,y1),(x2,y2)
ax1 + by1 + c = 0
ax2 + by2 + c = 0
==>
a(x1-x2) + b(y1-y2) = 0
a(x1-x2) = -b(y1-y2)
==>
a = y1-y2
b = x2-x1
c = x1y2 - x2y1

zoj 1084/poj 1129
贪心解决着色问题。当前点的临点颜色去除后,剩下能着色最小的颜色。

poj 1419
最大独立集,也是最大团问题。可以参考一下博客。
http://www.cnblogs.com/pushing-my-way/archive/2012/08/08/2627993.html
回溯法解释的不错,图G的最大独立团==图G补图的最大独立集。
最后打出来了,但是没有max赋初值wa了半天。。。%>_<%

p3692
最大团==补图最大独立数
二分图: 最大独立数 + 最小点覆盖数 = 顶点数
正好又做到了Hungary,那么就贴一下模板吧

static int mark[], cx[], cy[], nx, ny;
static void init(int a, int b) {
nx = a;
ny = b;
cx = new int[nx + 1];
Arrays.fill(cx, -1);
cy = new int[ny + 1];
Arrays.fill(cy, -1);
mark = new int[ny + 1];
}
static int hungary() {
int ans = 0;
for (int i = 1; i <= nx; i++) {
if (cx[i] == -1) {
Arrays.fill(mark, 0);
ans += path(i);
}
}
return ans;
}
static int path(int u) {
for (int v = 1; v <= ny; v++) {
if (map[u][v] == 1 && mark[v] == 0) {
mark[v] = 1;
if (cy[v] == -1 || path(cy[v]) == 1) {
cx[u] = v;
cy[v] = u;
return 1;
}
}
}
return 0;
}


混合欧拉回路/通路问题:
hdu 3472这道题目正好让我重新温习了一下如何解决混合图欧拉回路/混合图欧拉通路问题。
先说hdu 3472。按照顺序来
1、我们需要进行判断该图是否连通(并查集解决)
2、我们可以先給任意无相边设定一个假设的方向,比如都是<u,v>,同时按照我们的假设,计算每个顶点难道入度出度
3、我们可以在对于每条有相边,我们在网络流中建一条<u,v>,容量为1的边;而对于无相边,我们在网络流中则要建立两条边,分别是<u,v>和<v,u>,容量都为1。
4、统计每个顶点的入度、出度之差  
(1)如果有多于两个顶点的入度、出度之差为奇数,那么肯定不能组成欧拉通路
(2)如果有正好两个顶点的入度出度为奇数,那么很有可能这两个数就组成了欧拉通路,但我们只能判断 欧拉回路,所以我们采取的策略就是将这两个顶点相连,一个作为出度、一个作为入度
(3)没有顶点的度数为奇数,那么正好符合我们判断欧拉通路的要求
5、再次进行统计每个顶点的入度、出度,将入度大于出度的顶点和汇点相连,将出度大于入度的顶点和源点相连,同时容量为入度、出度之差的绝对值的一半。
6、做一次最大流,判断是否漫流即可。
这只是我自己最初的做法,但是后来我发现和网上的题解不一致,所以反复验证之后,做出了以下的修改。
3、我们只记录每条无相边,方向是我们假定的方向,容量为1。
4、(2)中,我们将两个出入度之差为奇数的顶点相连的时候,在网络流当中也需要连接。
这样才是最终的做法:
1、我们需要进行判断该图是否连通(并查集解决)
2、我们可以先給任意无相边设定一个假设的方向,比如都是<u,v>,同时按照我们的假设,计算每个顶点难道入度出度
3、我们只记录每条无相边,方向是我们假定的方向,容量为1
4、统计每个顶点的入度、出度之差  
(1)如果有多于两个顶点的入度、出度之差为奇数,那么肯定不能组成欧拉通路  
(2)如果有正好两个顶点的入度出度为奇数,那么很有可能这两个数就组成了欧拉通路,但我们只能判断欧拉回路,所以我们采取的策略就是将这两个顶点相连,一个作为出度、一个作为入 度,同时将这两个点在网络流当中连接(这一点非常重要)
(3)没有顶点的度数为奇数,那么正好符合我们判断欧拉通路的要求
5、再次进行统计每个顶点的入度、出度,将入度大于出度的顶点和汇点相连,将出度大于入度的顶点和源点相连,同时容量为入度、出度之差的绝对值的一半。
6、做一次最大流,判断是否漫流即可。



poj  1637
同样的做法,而且比上面那题简单,很裸,直接打就行。

poj 2230
static void dfs(int u) {
for (int i = head[u]; i != -1; i = edge[i].next) {
if (visit[i] == 1)
continue;
int v = edge[i].v;
visit[i] = 1;
dfs(v);
}
out.println(u);
}


poj 1041
输出边序最小的欧拉回路。从1出发,到1回。
还是看了毛杰大神的模板做出来的东西。
既然要求我们输出字典序最小,我们是将所有的边按照序号降序读入,然后用dfs输出即可。


poj 2337
还是欧拉回路,不过每一条边都是由单词组成。要求我们输出字典序最小的欧拉通路。这道题目这么来分解:
1、读入每条边,同时进行排序;排序结束后再进行加边。注意到我们最后输出要求字典序最小——所以我们采取的策略就是将每条边按照逆序读入、加边
2、全图是否连通
3、是否只有一点出度大于入度1,只有一点入度大于出度1,否则无欧拉通路
4、出度大于入度的点是出发点;如果所有点的入度、出度都相等,那么出发点就是最小的顶点——也就是在记录边字符串第一个字符,比如record[0].charAt(0)
5、然后就可以开始找了,
static void dfs(int u) {
for (int i = head[u]; i != -1; i = edge[i].next) {
if (edge[i].mark)
continue;
edge[i].mark = true;
dfs(edge[i].v);
ans[num++] = i;
}
}
6、在输出的时候需要注意一下,因为我直接用String record[]来进行记录数组,所以输出的时候需要再将它进行一定的反序,如下:
for (int i = num - 1; i > 0; i--)
out.print(record[n - 1 - ans[i]] + ".");
out.println(record[n - 1 - ans[0]]);



贴一个欧拉回路的模板:
DFS版本
static class Euler {
static int n, m;
static int indegree[], outdegree[];
static int ans[], num;
static int head[], toll;
static edge edge[];
Euler(int nn, int mm) {
this.n = nn;
this.m = mm;
head = new int[n + 1];
indegree = new int[n + 1];
outdegree = new int[n + 1];
edge = new edge[m];
// 这里要注意ans有可能会越界
// ans = new int[n * n + 1];
}
static void init() {
Arrays.fill(indegree, 0);
Arrays.fill(outdegree, 0);
toll = 0;
Arrays.fill(head, -1);
n = 0;
num = 0;
}
static void addEdge(int u, int v) {
edge[toll] = new edge(v);
edge[toll].next = head[u];
head[u] = toll++;
outdegree[u]++;
indegree[v]++;
}
// 这里的加边的id是为了预防出现要求按照边的序号输出
static void addEdge(int u, int v, int id) {
edge[toll] = new edge(v, id);
edge[toll].next = head[u];
head[u] = toll++;
outdegree[u]++;
indegree[v]++;
}
static class edge {
int v, next, id;
boolean mark;
edge(int v, int id) {
this.v = v;
this.id = id;
mark = false;
}
edge(int v) {
this.v = v;
mark = false;
}
}
void dfs(int u) {
for (int i = head[u]; i != -1; i = edge[i].next) {
if (edge[i].mark)
continue;
edge[i].mark = true;
// 本道题目需要我们将每条边遍历两次,所以这里可以不用这么写
// edge[i ^ 1].mark = true;
dfs(edge[i].v);
}
out.println(u);
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值