1. 程式人生 > >USACO 6.5 Betsy's Tour (插頭dp)

USACO 6.5 Betsy's Tour (插頭dp)

tours print eve 出現 png lin per fine 麻煩

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)