SGU 101.Domino (欧拉路)
时间限制: 0.5 sec
空间限制: 4096 KB
描述
多米诺骨牌,一种用小的方的木块或其他材料,每个都被一些点在面上标记,这些木块通常被称为骨牌。每个骨牌的面都被一条线分成两个 方形,两边各有一定点数。
N个多米诺骨牌,每个骨牌左右两侧分别有一个0~6的整数(骨牌可以旋转以调换其左右两数),
求一种把这些骨牌从左到右排列的方案,使得所有相邻的两数字相等(即左边骨牌右侧的数字等于右边骨牌左侧的数字)。
输入
第一行是一个整数N(1 ≤ N ≤ 100),表示骨牌的数量。接下来的N行描述每块骨牌,每块左右两边有不同的点数。
输出
如果无法安排,输出“No solution”。如果可能,输出任何一种,每行有一个数字,和“+”或“-”,前者代表不旋转,后者代表旋转。
样例输入
5
1 2
2 4
2 4
6 4
2 1
样例输出
2 -
5 +
1 +
3 +
4 -
{================================}
分析:
要对牌进行排序,上图对应样例,红色标记的是牌的编号,绿色圈出来的是需要翻转的牌。
如果把每一张牌当做一个节点,有相同数字的两张牌之间连接一条边,如下图
那么只需要找到一条路径经过所有节点,并且只经过每个节点一次。即一个图的哈密顿路径
找到一个图的哈密顿路径的时间复杂度的是O(n^2),而判断哈密顿路径的存在却是NP完全问题,而题目的时限只有0.5s,显然不能满足我们的要求.是否有更好的方法呢?
我们换种方式建图:
考虑:把每个数字当做一个节点,每张牌当做一条边。
如图
那么现在的目标是找到一条路径,经过每一条边且只经过一次,即图的欧拉路
题中最多有0~6,7个节点,100条边,如果使用朴素的DFS的话,还是会超时,这时要充分运用欧拉路的性质进行剪枝
(1)无向欧拉图度为奇的节点数为0或2;
(2)欧拉图从奇度节点出发,如果没有任意一个偶度节点都能行;
经过测试,如果构图时使用链式前向星,枚举边进行DFS还是会在较大的数据上超时
所以还需要进一步优化
枚举顶点,如果两个节点之间有多条边的话,可以进行边的剪枝,
如果a,b两点有多条边,从节点a搜到节点b时,不能从b节点找到符合条件的路径
那么这时a和b其他的边也可以不搜了,直接搜下一个对顶点 例如 a和c。
由于最多只有7个顶点,21个点对。这样时间上便非常充裕了。
参考代码:(后面有更优的fleury算法)
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;
const int INF = 211;
//Edge记录点对间边的编号
vector<int> Edge[7][7];
int eSum[7], Path[INF], pd[INF];
int n, x, y;
int Find (int x, int cur) {
if (cur == n) return 1;
for (int i = 0; i <= 6; i++){
for (int j = 0; j < Edge[x][i].size(); j++) {
int num = (int) abs (Edge[x][i][j]);
if (pd[num] == 0) {
//Path保存路径,pd[]标记边的使用情况
Path[cur] = Edge[x][i][j], pd[num] = 1;
if (Find (i, cur + 1) ) return 1;
pd[num] = 0; break;
}
}
}
return 0;
}
int main() {
scanf ("%d", &n);
for (int i = 1; i <= n; i++) {
scanf ("%d %d", &x, &y);
//正负号记录使用的是正边还是反边
eSum[x]++, Edge[x][y].push_back (i);
eSum[y]++, Edge[y][x].push_back (-i);
}
x = 0, y = 0;
//x记录奇度节点个数
for (int i = 0; i <= 6; i++) if (eSum[i] & 1) x++;
if (x == 0 || x == 2) {
for (int i = 0; i <= 6; i++) {
//去掉不符合要求的起点
if (eSum[i] == 0) continue;
if (x == 2 && (eSum[i] & 1) == 0) continue;
if (Find (i, 0) ) break;
//如果有解,那么符合要求的任意起点都能找到解
//所以只需一次Find()即可
puts ("No solution");
return 0;
}
for (int i = 0; i <= n - 1; i++)
printf ("%d %c\n", (int) abs (Path[i]), Path[i] < 0 ? '-' : '+');
}
else
puts ("No solution");
return 0;
}
一个多月之前并不知道欧拉路的fleury算法,用了DFS加剪枝优化,SGU上代码运行的时间为32ms。
在了解了欧拉路O(m)的算法后,又把这道题重写了一遍,时间为15ms,内存也从246kb减少了到110kb。 code
#include <iostream>
#include <cstdio>
using namespace std;
struct node {
int v, ne, sta, id;
} edge[210];
int deg[7], head[7], cnt = 1, ans[210][2], vis[210], tol;
int n, m, x, y;
//链式前向星存边
void addedge (int u, int v, int id, int sta) {
edge[++cnt].v = v; edge[cnt].id = id;
edge[cnt].ne = head[u], edge[cnt].sta = sta;
head[u] = cnt;
}
void EulerianP (int x) {
for (int i = head[x]; i != 0; i = edge[i].ne) {
int v = edge[i].v, p = edge[i].id, sta = edge[i].sta;
if (!vis[i]) {
//标记走过的边,不再删除标记!
vis[i] = vis[i ^ 1] = 1;
EulerianP (v);
ans[++tol][0] = p;
ans[tol][1] = sta;
}
}
}
int main() {
scanf ("%d", &n);
for (int i = 1; i <= n; i++) {
scanf ("%d %d", &x, &y);
deg[x]++, deg[y]++;
addedge (x, y, i, 1);//正向边记为1
addedge (y, x, i, 0);//反向边记为0
}
int sum = 0, s = -1;
//统计奇度点
for (int i = 0; i <= 6; i++)
if (deg[i] & 1) sum++, s = i;
if (sum != 0 && sum != 2) {
puts ("No solution");
return 0;
}
if (s != -1) EulerianP (s);
else//寻找起点
for (int i = 0; i <= 6; i++)
if (deg[i] > 0) {
EulerianP (i);
break;
}
if (tol != n) {
puts ("No solution");
return 0;
}
for (int i = n; i >= 1; i--)
printf ("%d %c\n", ans[i][0], ans[i][1] == 1 ? '+' : '-');
return 0;
}
http://www.cnblogs.com/keam37/ keam所有 转载请注明出处
SGU 101.Domino (欧拉路)的更多相关文章
- SGU 101 Domino (输出欧拉路径)
101. Domino time limit per test: 0.25 sec. memory limit per test: 4096 KB Dominoes – game played wit ...
- sgu 101 Domino 解题报告及测试数据
101. Domino time limit per test: 0.25 sec. memory limit per test: 4096 KB 题解: 求多米诺骨牌按照一定方式放置能否使相邻的位置 ...
- sgu 101 domino
题意还算简洁明了,加上有道翻译凑过着读完了题.题意大体上是 给你 n 个多米诺骨牌, 给出每个骨牌两端的数字, 只有数字相同才可以推到, 比如 2-3和3-2.你可以旋转这些多米诺骨牌, 输出一个可以 ...
- SGU 101.Domino( 欧拉路径 )
求欧拉路径...直接dfs即可,时间复杂度O(N) -------------------------------------------------------------------------- ...
- SGU 101 Domino【欧拉路径】
题目链接: http://acm.sgu.ru/problem.php?contest=0&problem=101 题意: N个多米诺骨牌,每个骨牌左右两侧分别有一个0~6的整数(骨牌可以旋转 ...
- SGU 101 Domino 题解
鉴于SGU题目难度较大,AC后便给出算法并发布博文,代码则写得较满意后再补上.——icedream61 题目简述:暂略 AC人数:3609(2015年7月20日) 算法: 这题就是一笔画,最多只有7个 ...
- SGU 156. Strange Graph(欧拉路)
时间限制:0.25s 空间限制:6M 题目描述 让我们想象一个无向图G=<V,E>.如果边(u,v)在边集E中,那么我们就说两个顶点u和v是邻接点.在这种情况下,我们也说u是v的一个邻接点 ...
- ACM: SGU 101 Domino- 欧拉回路-并查集
sgu 101 - Domino Time Limit:250MS Memory Limit:4096KB 64bit IO Format:%I64d & %I64u Desc ...
- 洛谷P1341 无序字母对[无向图欧拉路]
题目描述 给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒).请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现. 输入输出格式 输入格式: 第一行输入一 ...
随机推荐
- 如何给循环中的对象添加事件--深入理解JavaScript的闭包特性
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- maya绝招(41--60)
第41招 捕捉和旋转 从MAYA5开始,双击工具箱中的移动缩放旋转工具,马上就可以调出工具属性栏.以旋转为例,将Snap Rotate勾选,并设置Step Size数值,就可以旋转特定的数值了 第42 ...
- 如何解决缺少OCX问题,如何在win7 64位下注册OCX
最近原来的系统很慢,重装win7. 今天跑文章格式化编辑器,结果提示找不到Comctl32.ocx. 1. 上网搜索下载Comctl32.ocx,直接拷贝到c:\windows\system32不行, ...
- Apache+PHP 环境上传文件配置
打开php.ini 配置文件,查找 File Uploads ,在这个区域有以下3个选项: file_uploads = On 是否允许HTTP文件上传.默认值为On允许HTTP文件上传,此选项不能设 ...
- iOS不越狱装收费App——注册iOS设备为开发者工具
额,这篇教程主要是我写下来用于总结注册iOS设备和用iResign安装App的过程,想要不越狱安装App当然有办法,但是有几个前提--你是一个Apple开发者,或者你有个朋友是App的开发者.如果没有 ...
- 【C++】冒泡排序、插入排序、快速排序
#include<iostream> using namespace std; void BubbleSort(int *a,int istart,int len)//冒泡排序 { //a ...
- nandflash中oob、ecc分析
1.为何需要分析? 最近一直接触这类驱动,如果对它的原理不懂的话,驱动调试会很麻烦!!!!!! 2.ecc? nand的纠错能力,目前有1位.4位和8位,也就是说在512字节中如果是4位的ecc那就可 ...
- Android编程动态创建视图View的方法
在Android开 发中,在Activity中关联视图View是一般使用setContentView方法,该方法一种参数是使用XML资源直接创 建:setContentView (int layout ...
- 《Java并发编程实战》第六章 任务运行 读书笔记
一. 在线程中运行任务 无限制创建线程的不足 .线程生命周期的开销很高 .资源消耗 .稳定性 二.Executor框架 Executor基于生产者-消费者模式.提交任务的操作相当于生产者.运行任务的线 ...
- 使用blktrace统计磁盘块I/O访问频率 + IO调度CFQ
http://blog.chinaunix.net/uid-24774106-id-4096470.html http://blog.csdn.net/wyzxg/article/details/74 ...