Betsy's Tour
Don Piele

A square township has been divided up into N2 square plots (1 <= N <= 7). The Farm is located in the upper left plot and the Market is located in the lower left plot. Betsy takes her tour of the township going from Farm to Market by walking through every plot exactly once. Shown below is one possible tour for Betsy when N=3.

----------------
| | | |
| F********** |
| | | * |
------------*---
| | | * |
| ***** | * |
| * | * | * |
---*---*----*---
| * | * | * |
| M | ****** |
| | | |
----------------

Write a program that will count how many unique tours Betsy can take in going from Farm to Market for any value of N.

PROGRAM NAME: betsy

INPUT FORMAT

Line 1: A single integer N (1 <= N <= 7)

SAMPLE INPUT (file betsy.in)

3

OUTPUT FORMAT

A single line with a single integer, the number of unique tours.

SAMPLE OUTPUT (file betsy.out)

2

————————————————————题解

大意是从方块的左上角走到左下角,求一条哈密顿路

由于插头dp求哈密顿路需要一堆独立插头什么的,很麻烦,然后我们从起点到终点加一排,求哈密顿回路

然后我们发现我们再把这个图顺时针转90°它就会很舒服……

好啦,今天新学的一个算法……记录一下

求一个n*m图的哈密顿回路(哈密顿回路就是从一个点出发,图上的每一个点都经历一遍,再回到原点)

我们把一个格子向四周延伸记为插头

这样的话格子要么延伸,要么不延伸,如果延伸的话,它可以向四个方向延伸。

但是因为哈密顿回路嘛,我们如果有了一个延伸,肯定还要另一个延伸和它一起构成一个回路,很圆满,像这样。

这里应用括号匹配的概念,我们用0表示没有延伸,1表示( , 2表示)

我们从上到下,从左到右枚举,对于一个新格子,我们需要知道它上方的格子的延伸情况,左方的格子延伸情况

当前格子设为(i,j)

如果上0,左0

那么当前格子必须两个方向都延伸

它对(i+1,j)的影响为1,对(i,j+1)的影响为2

ps:画的横线只是为了显示上左两个格子对第三个的影响,并不一定是用这条线段填充的格子

如果上0,左1

我们可以把左边的1过继过去

(对右面影响为1)

向下弯也一样(对下面影响为1)

同理,如果上0左2,我们就把左边的2过继过去

如果上1或2,左0,我们把上面的1或2过继过去

如果上1,左1

上左都有延伸当然是要把它们都连起来啦!然后找上方的1对应的2,将这个2更改成1(也就是橙色的括号更改)

上2左2和这是同理的,不过我们要找左边的2对应的1,将这个1更改成2

如果上1左2

简单地删掉就可以了

如果上2左1

恭喜你你找到答案了!因为这种情况只能出现在我们枚举到右下角填上哈密顿回路的最后一角!然后累加就可以了!

这道题就可以0.000过了

但是按照USACO的说法,这题就是个暴搜加优化,6章的剪枝真是多到吐……换换口味……

 /*
LANG: C++
PROG: betsy
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define siji(i,x,y) for(int i=(x) ; i <= (y) ; ++i)
#define xiaosiji(i,x,y) for(int i = (x) ; i < (y); ++i)
#define gongzi(j,x,y) for(int j = (x) ; j >= (y) ; --j)
#define ivorysi
#define mo 11447
#define eps 1e-8
#define o(x) ((x)*(x))
using namespace std;
typedef long long ll;
/*
0:#
1:(
2:)
3:
*/
int n,ans;
int sum[][],hash[],code[][],base[],tot[];
//用hash是因为可能会有不同的原始状态转移到同一个新状态
//sum存储的是code[cur][i]情况数总和
//code[cur][i]是四进制的压位后的编码
void put_in_hash(int cur,int op,int data) {
if(!hash[op]) {
code[cur][++tot[cur]]=op;
hash[op]=tot[cur];
}
sum[cur][hash[op]]+=data;
}//这题很小,hash直接开就好,要不然还得挂link不断取模什么的
int Change_state(int state,int pos,int val) {
return state+val*(<<base[pos]);
}
int Get(int state,int pos) {
return (state>>base[pos])&;
}
void solve() {
scanf("%d",&n);
if(n==) {puts("");return;}
base[]=;
siji(i,,) {
base[i]=base[i-]+;
}
int cur=;
code[][]=Change_state(code[][],,);
code[][]=Change_state(code[][],n,);//把多添的一行的第一个设成1,第n个设成2,求回路
sum[cur][]=,tot[cur]=;
siji(i,,n) {
siji(j,,n) {
cur^=;//换情况
tot[cur]=;
memset(hash,,sizeof(hash));
memset(sum[cur],,sizeof(sum[cur]));
siji(k,,tot[cur^]) {
int state=code[cur^][k];
if(j==) state<<=;//移到1这一位相当于多了一个轮廓线
int p=Get(state,j+),q=Get(state,j);//p上方 q左方
state=Change_state(state,j+,-*p);state=Change_state(state,j,-*q);
//将拿出来的p、q删除
if(p== && q==) {
if(j!=n && i!=n) {
int change=Change_state(state,j,);//向下延伸
change=Change_state(change,j+,);//向右延伸
put_in_hash(cur,change,sum[cur^][k]);
}
}
if(p== && q>) {
if(j!=n){
int change=Change_state(state,j+,q);//向右延伸
put_in_hash(cur,change,sum[cur^][k]);
}
if(i!=n) {
int change=Change_state(state,j,q);//向下延伸
put_in_hash(cur,change,sum[cur^][k]);
}
}
if(p> && q==) {
if(j!=n) {
int change=Change_state(state,j+,p);//向右延伸
put_in_hash(cur,change,sum[cur^][k]);
}
if(i!=n) {
int change=Change_state(state,j,p);//向下延伸
put_in_hash(cur,change,sum[cur^][k]);
}
}
if(p== && q==) {
int change=state,l,top;
for(l = j+ ,top=;top;++l) {
if(Get(change,l)==) ++top;
if(Get(change,l)==) --top;
}
//不能直接找右侧第一个右括号,因为可能有这样((()))()()
//删掉前两个括号时,我们需要找第5个括号更改方向
change=Change_state(change,l-,-);//2-1=1
//2-1=1
//相当于把原值删除加上新值
put_in_hash(cur,change,sum[cur^][k]);
}
if(p== && q==) {
int change=state,l,top;
for(l = j- ,top=; top ;--l) {
if(Get(change,l)==) ++top;
if(Get(change,l)==) --top;
}
change=Change_state(change,l+,);//1+1=2
put_in_hash(cur,change,sum[cur^][k]);
}
if(p== && q==) {
int change=state;
put_in_hash(cur,change,sum[cur^][k]);
}
if(p==&&q==) {
if(i==n && j==n) ans+=sum[cur^][k];
}
}
}
}
printf("%d\n",ans);
}
int main(int argc, char const *argv[])
{
#ifdef ivorysi
freopen("betsy.in","r",stdin);
freopen("betsy.out","w",stdout);
#else
freopen("f1.in","r",stdin);
//freopen("f1.out","w",stdout);
#endif
solve();
return ;
}

今天中考刚刚报完志愿,结果我又来敲了一天代码……我到底中考想考啥样……

USACO 6.5 Betsy's Tour (插头dp)的更多相关文章

  1. USACO 5.4 Betsy's Tour(暴力)

    水过,水过,这个程序跑7,跑5分钟左右把... /* ID: cuizhe LANG: C++ TASK: betsy */ #include <iostream> #include &l ...

  2. ZOJ 3256 Tour in the Castle 插头DP 矩阵乘法

    题解 这题是一道非常好的插头题,与一般的按格转移的题目不同,由于m很大,要矩阵乘法,这题需要你做一个按列转移的插头DP. 按列转移多少与按格转移不同,但大体上还是基于连通性进行转移.每一列只有右插头是 ...

  3. POJ 1739 Tony's Tour (插头DP,轮廓线DP)

    题意:给一个n*m的矩阵,其中#是障碍格子,其他则是必走的格子,问从左下角的格子走到右下角的格子有多少种方式. 思路: 注意有可能答案是0,就是障碍格子阻挡住了去路. 插头DP有两种比较常见的表示连通 ...

  4. 插头DP专题

    建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...

  5. HDU 1693 Eat the Trees(插头DP)

    题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...

  6. 插头DP题目泛做(为了对应WYD的课件)

    题目1:BZOJ 1814 URAL 1519 Formula 1 题目大意:给定一个N*M的棋盘,上面有障碍格子.求一个经过所有非障碍格子形成的回路的数量. 插头DP入门题.记录连通分量. #inc ...

  7. 动态规划之插头DP入门

    基于联通性的状态压缩动态规划是一类非常典型的状态压缩动态规划问题,由于其压缩的本质并不像是普通的状态压缩动态规划那样用0或者1来表示未使用.使用两种状态,而是使用数字来表示类似插头的状态,因此.它又被 ...

  8. [入门向选讲] 插头DP:从零概念到入门 (例题:HDU1693 COGS1283 BZOJ2310 BZOJ2331)

    转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识……这真的是一种很锻炼人的题型…… 每一道题的状态都不一样 ...

  9. 省选算法学习-插头dp

    插头dp?你说的是这个吗? 好吧显然不是...... 所谓插头dp,实际上是“基于连通性的状态压缩dp”的简称,最先出现在cdq的论文里面 本篇博客致力于通过几道小小的例题(大部分都比较浅显)来介绍一 ...

随机推荐

  1. C/C++中的回调函数

    在理解“回调函数”之前,首先讨论下函数指针的概念. 函数指针 (1)概念:指针是一个变量,是用来指向内存地址的.一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件 ...

  2. angularjs的Controller as

    <html ng-app="notesApp"> <head><title>Notes App</title></head&g ...

  3. spring-data-redis,jedis和redis主从集成和遇到的问题

    Redis主从加哨兵的部署详见http://www.cnblogs.com/dupang/p/6414365.html spring-data-redis和jedis集成代码总体结构 代码地址http ...

  4. Git之修复Bug流程

    场景描述 当一个项目已经上线,同时又在原有基础上新增功能模块,于是乎就要在原有代码的基础上进行开发,在新增模块功能的开发的过程中,项目发现了一个紧急Bug,需要修复.应对这种情况,有以下两种解决方案: ...

  5. SQL Server 2008 R2 企业版安装教程

    1 安装包解压 2 解压后,打开setup.exe文件,选择安装,显示如图: 3 选择全新安装或向现有安装添加功能 4 点确定 5 输入 企业版序列号:R88PF-GMCFT-KM2KR-4R7GB- ...

  6. R6—单变量正态性检验

    方法不唯一 单变量正态检验主要的话包括以下这些 shapiro.test();#Shapiro-Wilk检验 library("nortest"); lillie.test() # ...

  7. 20155232 2016-2017-3 《Java程序设计》第7周学习总结

    20155232 2016-2017-3 <Java程序设计>第7周学习总结 教材学习内容总结 第十三章 1.Greenwich MeanTime,格林威治时间,简称GMT时间,由观察太阳 ...

  8. 针对用户在个人中心绑定手机认证的一些js代码。

    需求: 1:手机号码校验(格式的校验,手机号码是否已经绑定过)---未实现 2:填完手机号码,点击发送验证码,手机会收到一条信息 3:发送验证码按钮不可用,变成重新发送的倒计时 1):60秒以后又可以 ...

  9. 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)

    需求: 编写一个diff工具,用于判断两个目录下所有的改动 详细介绍: 有A和B两个目录,目录所在位置及层级均不确定 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加.修改.删除),将有改 ...

  10. Java基础break、continue语句的用法

    break适用范围:只能用于switch或者是循环语句中.当然可以用于增强for循环. break作用: 1. break用于switch语句的作用是结束一个switch语句. 2. break用于循 ...