USACO 6.5 Betsy's Tour (插头dp)

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章的剪枝真是多到吐……换换口味……

  1 /*
  2 LANG: C++
  3 PROG: betsy
  4 */
  5 #include <iostream>
  6 #include <cstdio>
  7 #include <algorithm>
  8 #include <cstring>
  9 #include <cmath>
 10 #define siji(i,x,y) for(int i=(x) ; i <= (y) ; ++i)
 11 #define xiaosiji(i,x,y) for(int i = (x) ; i < (y); ++i)
 12 #define gongzi(j,x,y) for(int j = (x) ; j >= (y) ; --j)
 13 #define ivorysi
 14 #define mo 11447
 15 #define eps 1e-8
 16 #define o(x) ((x)*(x))
 17 using namespace std;
 18 typedef long long ll;
 19 /*
 20 0:#
 21 1:(
 22 2:)
 23 3:
 24 */
 25 int n,ans;
 26 int sum[2][66000],hash[66000],code[2][66000],base[10],tot[2];
 27 //用hash是因为可能会有不同的原始状态转移到同一个新状态
 28 //sum存储的是code[cur][i]情况数总和
 29 //code[cur][i]是四进制的压位后的编码
 30 void put_in_hash(int cur,int op,int data) {
 31     if(!hash[op]) {
 32         code[cur][++tot[cur]]=op;
 33         hash[op]=tot[cur];
 34     }
 35     sum[cur][hash[op]]+=data;
 36 }//这题很小,hash直接开就好,要不然还得挂link不断取模什么的
 37 int Change_state(int state,int pos,int val) {
 38     return state+val*(1<<base[pos]);
 39 }
 40 int Get(int state,int pos) {
 41     return (state>>base[pos])&3;
 42 }
 43 void solve() {
 44     scanf("%d",&n);
 45     if(n==1) {puts("1");return;}
 46     base[1]=0;
 47     siji(i,2,9) {
 48         base[i]=base[i-1]+2;
 49     }
 50     int cur=0;
 51     code[0][1]=Change_state(code[0][1],1,1);
 52     code[0][1]=Change_state(code[0][1],n,2);//把多添的一行的第一个设成1,第n个设成2,求回路
 53     sum[cur][1]=1,tot[cur]=1;
 54     siji(i,1,n) {
 55         siji(j,1,n) {
 56             cur^=1;//换情况
 57             tot[cur]=0;
 58             memset(hash,0,sizeof(hash));
 59             memset(sum[cur],0,sizeof(sum[cur]));
 60             siji(k,1,tot[cur^1]) {
 61                 int state=code[cur^1][k];
 62                 if(j==1) state<<=2;//移到1这一位相当于多了一个轮廓线
 63                 int p=Get(state,j+1),q=Get(state,j);//p上方 q左方
 64                 state=Change_state(state,j+1,-1*p);state=Change_state(state,j,-1*q);
 65                 //将拿出来的p、q删除
 66                 if(p==0 && q==0) {
 67                     if(j!=n && i!=n) {
 68                         int change=Change_state(state,j,1);//向下延伸
 69                         change=Change_state(change,j+1,2);//向右延伸
 70                         put_in_hash(cur,change,sum[cur^1][k]);
 71                     }
 72                 }
 73                 if(p==0 && q>0) {
 74                     if(j!=n){
 75                         int change=Change_state(state,j+1,q);//向右延伸
 76                         put_in_hash(cur,change,sum[cur^1][k]);
 77                     }
 78                     if(i!=n) {
 79                         int change=Change_state(state,j,q);//向下延伸
 80                         put_in_hash(cur,change,sum[cur^1][k]);
 81                     }
 82                 }
 83                 if(p>0 && q==0) {
 84                     if(j!=n) {
 85                         int change=Change_state(state,j+1,p);//向右延伸
 86                         put_in_hash(cur,change,sum[cur^1][k]);
 87                     }
 88                     if(i!=n) {
 89                         int change=Change_state(state,j,p);//向下延伸
 90                         put_in_hash(cur,change,sum[cur^1][k]);
 91                     }
 92                 }
 93                 if(p==1 && q==1) {
 94                     int change=state,l,top;
 95                     for(l = j+2 ,top=1;top;++l) {
 96                         if(Get(change,l)==1) ++top;
 97                         if(Get(change,l)==2) --top;
 98                     }
 99                     //不能直接找右侧第一个右括号,因为可能有这样((()))()()
100                     //删掉前两个括号时,我们需要找第5个括号更改方向
101                     change=Change_state(change,l-1,-1);//2-1=1
102                     //2-1=1
103                     //相当于把原值删除加上新值
104                     put_in_hash(cur,change,sum[cur^1][k]);
105                 }
106                 if(p==2 && q==2) {
107                     int change=state,l,top;
108                     for(l = j-1 ,top=1; top ;--l) {
109                         if(Get(change,l)==2) ++top;
110                         if(Get(change,l)==1) --top;
111                     }
112                     change=Change_state(change,l+1,1);//1+1=2
113                     put_in_hash(cur,change,sum[cur^1][k]);
114                 }
115                 if(p==1 && q==2) {
116                     int change=state;
117                     put_in_hash(cur,change,sum[cur^1][k]);
118                 }
119                 if(p==2&&q==1) {
120                     if(i==n && j==n) ans+=sum[cur^1][k];
121                 }
122             }
123         }
124     }
125     printf("%d\n",ans);
126 }
127 int main(int argc, char const *argv[])
128 {
129 #ifdef ivorysi
130     freopen("betsy.in","r",stdin);
131     freopen("betsy.out","w",stdout);
132 #else
133     freopen("f1.in","r",stdin);
134     //freopen("f1.out","w",stdout);
135 #endif
136     solve();
137     return 0;
138 }

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

USACO 6.5 Betsy's Tour (插头dp)

时间: 2024-11-09 18:45:38

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

poj 1739 Tony&#39;s Tour 插头dp模板题

题意: 给一个迷宫,求左下角到右下角的路径数. 分析: 插头dp的模板题,建议先看cdq的论文再看代码,这份代码在模板基础上略微有改动.论文地址http://wenku.baidu.com/view/ed2b3e23482fb4daa58d4b74.html 代码: #include <iostream> using namespace std; const int maxD=16; const int HASH=10007; const int STATE=1000024; int N,M;

【插头DP】 POJ 1739 Tony&#39;s Tour

通道:http://poj.org/problem?id=1739 题意:左下角走到右下角路径数,单回路.做法就是我们新添2行后寻找回路就可以啦 . 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int MAX_N = 13; 8 const int MAX_M = 13; 9 const int HASH =

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

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识……这真的是一种很锻炼人的题型…… 每一道题的状态都不一样,并且有不少的分类讨论,让插头DP十分锻炼思维的全面性和严谨性. 下面我们一起来学习插头DP的内容吧! 插头DP主要用来处理一系列基于连通性状态压缩的动态规划问题,处理的具体问题有很多种,并且一般数据规模较小. 由于棋盘有很特殊的结构,使得它可以与“连通性”有很强的联系,因此插头DP最常见的应用要数

插头DP小结

首先是CDQ<基于连通性状态压缩的动态规划问题>论文上的题目: URAL 1519 Formula 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int maxn = 15; 6 const int HASH = 30007; 7 const int SIZE = 1000010; 8 typedef long lon

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通

动态规划之插头DP入门

基于联通性的状态压缩动态规划是一类很典型的状态压缩动态规划问题,因为其压缩的本质并不像是普通的状态压缩动态规划那样用0或者1来表示未使用.使用两种状态,而是使用数字来表示类似插头的状态,因此,它又被称作插头DP. 插头DP本质上是一类状态压缩DP,因此,依然避免不了其指数级别的算法复杂度,即便如此,它依然要比普通的搜索算法快很多. [例]Postal Vans(USACO training 6.1.1) 有一个4*n的矩阵,从左上角出发,每次可以向四个方向走一步,求经过每个格子恰好一次,再回到起

插头dp

对于网格中的dp可以用轮廓线,如果有一些格子不能走就可以用插头dp了. bzoj2331 地板 题目大意:用L型铺地n*m,有一些格子不能铺,求方案数. 思路:fi[i][j][s]表示铺到(i,j),轮廓线状态s,0表示没有插头,1表示插头没拐弯,2表示插头拐弯了,手动枚举转移. 注意:(1)按四进制好写: (2)因为实际状态和四进制的差很多,所以用hash表来存储,防止mle和tle,同时使用滚动数组. #include<iostream> #include<cstdio> #

插头DP学习

队内没人会插头DP,感觉这个不会不行...所以我还是默默去学了一下, 学了一天,感觉会了一点.对于每一行,一共有j+1个插头,如果是多回路类的题目, 比较简单,可以用1表示有插头,0表示没有插头,这样就可以愉快转移了, 对于当前出来的位置(i,j),与它有关的插头有j-1和j 那么我们可以枚举状态经行转移. 对于单回路的问题....只是了解思想,目前还不会写,太笨了=_= poj 2411 Mondriaan's Dream 题意:用1*2小方块组成n*m矩阵有多少种组成方式 思路:我们从上到下

BZOJ 2595 游览计划(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2595 题意:给出一个数字矩阵.求一个连通块包含所有的数字0且连通块内所有数字之和最小. 思路:对于每个格子,是0则必须要选.那么对于不选的格子(i,j)在什么时候可以不选呢?必须同时满足以下两个条件: (1)(i,j)不是0: (2)(i-1,j)不选或者(i-1,j)选了但是轮廓线上还有别的地方与(i-1,j)是一个连通块. int Pre[105][N],op[105][N]; s