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)