POJ2044 深搜+剪枝(云彩下雨)
生活随笔
收集整理的這篇文章主要介紹了
POJ2044 深搜+剪枝(云彩下雨)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:
? ? ? ?有一個城鎮,是4*4的大小的,然后你控制一塊云彩,2*2的,你每天可以有9種走的方法,上下左右,或者不動,走的時候可以走1或者2步,云彩所在的地方肯定會下雨,然后給你做多365天的安排,要求某些日子的某些城鎮不能下雨(因為有**節日),還有任何地方都不能有連續超過6天不下雨。
思路:
? ? ? ?首先9種走法,做多連續六點不下雨,這個可以直接DFS深搜,(BFS不知道行不行,沒試過,主要擔心的是內存,反正目測BFS隊列QUEUE占用的內存太大),如果不優化的話的話時間復雜度可是9^365根本受不了啊,然后就是考慮優化,也就是mark走過的狀態,關鍵是怎么標記這個狀態,一開始我是想找16個素數,然后根據每個格子的步數去計算每個16個步數得到的一個特別值去hash狀態,可以失敗了,找素數的方法不行,舉個例子 3 * 7 + 7 * 3 = 7 * 3 + 3 * 7,然后就是考慮狀態壓縮,可是按照最笨的放發的話狀態壓縮的9^16中狀態,后來我自己優化了下,還是9^12種狀態,還是不行,后來看了別人的解釋,一開始有疑惑,想了好久才說服自己,說下標程,可以只考慮四個點,假如當前用云彩的左上角表示云彩,那么我們只要考慮的四個點就是 1 1 ,1 3 ,3 1,3 3也就是云彩在四個角落的時候的坐標,判斷下雨天數的時候可以只判斷這四個,還有mark的時候也是mark[t][n][a][b][c][d] t 天數,a云彩當前位置(只記錄左上角,離散化之后就9個),a b c d分別是當前那4個關鍵位置連續沒下雨的天數,然后就直接DFS就行了,下面解釋下為什么這樣是對的:
(1)充分性 : 只要這四個點都滿足天數不大于7 那么全圖肯定都滿足,這個自己看下就知道了,因為把圖分成四塊,這四個關鍵點肯定是每一個塊中連續不下雨天數最長的。
(2)必要性 : 如果全圖都滿足連續不下雨情況,那么這四個關鍵點必然滿足,因為要想讓(1 ,1) ,(1 ,4) ,(4 ,1) ,(4 ,4)都滿足,那么必須走這4個關鍵點,別的點照顧不到這。
(3)還有就是只用這四個點代表唯一性是否正確,這個我猶豫了好久,就是這個地方一開始怎么也想不明白,現在說下我目前的理解。
假如當前狀態
1111 2222 ? ? ? ?1111 2222 ? 如果按照上面的那個方法mark必須證明這兩個狀態是一
1111 2222 ? ? ? ?1111 2222 ? 個狀態,我的理解是四個關鍵位置的連續不下雨天數一定
3333 4444 ? ? ? ?3332 4443 ? 比自己所在的模塊的所有天數都大,無論什么時候,那么
3333 4444 ? ? ? ?3332 4443 ? 也就是說如果以后出現問題肯定是這四個最大的中的某個
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?出現問題,也就是別的點永遠都只是陪襯,所以 陪襯的點
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?怎么樣不會影響總的狀態以及最終的趨勢,所以這種mark ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 的方法是正確的,這個是我自己的理解。
#include<stdio.h>
#include<string.h>
int DAY[367];
bool mark[366][10][8][8][8][8];
int ys[12] = {0 ,1 ,2 ,3 ,0 ,4 ,5 ,6 ,0 ,7 ,8 ,9};
int dir[9][2] = {0 ,0 ,0 ,1 ,0 ,-1 ,1 ,0 ,-1 ,0 ,0 ,2 ,0 ,-2 ,2 ,0 ,-2 ,0};
int OK ,T;
bool ok(int t ,int x ,int y ,int a[])
{
? ? int aa = (x - 1) * 4 + y;
? ? int bb= (x - 1 + 1) * 4 + y;
? ? int cc = (x - 1) * 4 + y + 1;
? ? int dd = (x - 1 + 1) * 4 + y + 1;
? ? if(x < 1 || x > 3 || y < 1 || y > 3)
? ? return 0;
? ? if(a[1] >= 7 || a[2] >= 7 || a[3] >= 7 || a[4] >= 7)
? ? return 0;
? ? if(((1 << aa) & DAY[t]) || ((1 << bb) & DAY[t]) || ((1 << cc) & DAY[t]) || ((1 << dd) & DAY[t]))
? ? return 0;
? ? if(mark[t][ys[aa]][a[1]][a[2]][a[3]][a[4]]) return 0;
? ? return 1;
}
void DFS(int t ,int x ,int y ,int a[])
{
? ? if(t == T) OK = 1;
? ? if(OK) return ;
? ? int b[5];
? ? for(int i = 0 ;i < 9 ;i ++)
? ? {
? ? ? ? if(t == 0 && i) break;
? ? ? ? int nowt = t + 1;
? ? ? ? int nowx = x + dir[i][0];
? ? ? ? int nowy = y + dir[i][1];
? ? ? ? for(int j = 1 ;j <= 4 ;j ++)
? ? ? ? b[j] = a[j] + 1;
? ? ? ? if(nowx == 1 && nowy == 1) b[1] = 0;
? ? ? ? if(nowx == 1 && nowy == 3) b[2] = 0;
? ? ? ? if(nowx == 3 && nowy == 1) b[3] = 0;
? ? ? ? if(nowx == 3 && nowy == 3) b[4] = 0;
? ? ? ? if(ok(nowt ,nowx ,nowy ,b))
? ? ? ? {
? ? ? ? ? ? mark[nowt][ys[(nowx-1)*4+nowy]][b[1]][b[2]][b[3]][b[4]] = 1;
? ? ? ? ? ? DFS(nowt ,nowx ,nowy ,b);
? ? ? ? }
? ? }
? ? return ;
}
int main ()
{
? ? int i ,j;
? ? while(~scanf("%d" ,&T) && T)
? ? {
? ? ? ? memset(DAY ,0 ,sizeof(DAY));
? ? ? ? for(i = 1 ;i <= T ;i ++)
? ? ? ? {
? ? ? ? ? ? int tmp;
? ? ? ? ? ? for(j = 1 ;j <= 16 ;j ++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? scanf("%d" ,&tmp);
? ? ? ? ? ? ? ? DAY[i] = DAY[i] | (tmp << j);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? memset(mark ,0 ,sizeof(mark));
? ? ? ? OK = 0;
? ? ? ? int a[5];
? ? ? ? a[1] = a[2] = a[3] = a[4] = 0;
? ? ? ? DFS(0 ,2 ,2 ,a);
? ? ? ? printf("%d\n" ,OK);
? ? }
? ? return 0;
}
? ? ? ?有一個城鎮,是4*4的大小的,然后你控制一塊云彩,2*2的,你每天可以有9種走的方法,上下左右,或者不動,走的時候可以走1或者2步,云彩所在的地方肯定會下雨,然后給你做多365天的安排,要求某些日子的某些城鎮不能下雨(因為有**節日),還有任何地方都不能有連續超過6天不下雨。
思路:
? ? ? ?首先9種走法,做多連續六點不下雨,這個可以直接DFS深搜,(BFS不知道行不行,沒試過,主要擔心的是內存,反正目測BFS隊列QUEUE占用的內存太大),如果不優化的話的話時間復雜度可是9^365根本受不了啊,然后就是考慮優化,也就是mark走過的狀態,關鍵是怎么標記這個狀態,一開始我是想找16個素數,然后根據每個格子的步數去計算每個16個步數得到的一個特別值去hash狀態,可以失敗了,找素數的方法不行,舉個例子 3 * 7 + 7 * 3 = 7 * 3 + 3 * 7,然后就是考慮狀態壓縮,可是按照最笨的放發的話狀態壓縮的9^16中狀態,后來我自己優化了下,還是9^12種狀態,還是不行,后來看了別人的解釋,一開始有疑惑,想了好久才說服自己,說下標程,可以只考慮四個點,假如當前用云彩的左上角表示云彩,那么我們只要考慮的四個點就是 1 1 ,1 3 ,3 1,3 3也就是云彩在四個角落的時候的坐標,判斷下雨天數的時候可以只判斷這四個,還有mark的時候也是mark[t][n][a][b][c][d] t 天數,a云彩當前位置(只記錄左上角,離散化之后就9個),a b c d分別是當前那4個關鍵位置連續沒下雨的天數,然后就直接DFS就行了,下面解釋下為什么這樣是對的:
(1)充分性 : 只要這四個點都滿足天數不大于7 那么全圖肯定都滿足,這個自己看下就知道了,因為把圖分成四塊,這四個關鍵點肯定是每一個塊中連續不下雨天數最長的。
(2)必要性 : 如果全圖都滿足連續不下雨情況,那么這四個關鍵點必然滿足,因為要想讓(1 ,1) ,(1 ,4) ,(4 ,1) ,(4 ,4)都滿足,那么必須走這4個關鍵點,別的點照顧不到這。
(3)還有就是只用這四個點代表唯一性是否正確,這個我猶豫了好久,就是這個地方一開始怎么也想不明白,現在說下我目前的理解。
假如當前狀態
1111 2222 ? ? ? ?1111 2222 ? 如果按照上面的那個方法mark必須證明這兩個狀態是一
1111 2222 ? ? ? ?1111 2222 ? 個狀態,我的理解是四個關鍵位置的連續不下雨天數一定
3333 4444 ? ? ? ?3332 4443 ? 比自己所在的模塊的所有天數都大,無論什么時候,那么
3333 4444 ? ? ? ?3332 4443 ? 也就是說如果以后出現問題肯定是這四個最大的中的某個
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?出現問題,也就是別的點永遠都只是陪襯,所以 陪襯的點
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?怎么樣不會影響總的狀態以及最終的趨勢,所以這種mark ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 的方法是正確的,這個是我自己的理解。
#include<stdio.h>
#include<string.h>
int DAY[367];
bool mark[366][10][8][8][8][8];
int ys[12] = {0 ,1 ,2 ,3 ,0 ,4 ,5 ,6 ,0 ,7 ,8 ,9};
int dir[9][2] = {0 ,0 ,0 ,1 ,0 ,-1 ,1 ,0 ,-1 ,0 ,0 ,2 ,0 ,-2 ,2 ,0 ,-2 ,0};
int OK ,T;
bool ok(int t ,int x ,int y ,int a[])
{
? ? int aa = (x - 1) * 4 + y;
? ? int bb= (x - 1 + 1) * 4 + y;
? ? int cc = (x - 1) * 4 + y + 1;
? ? int dd = (x - 1 + 1) * 4 + y + 1;
? ? if(x < 1 || x > 3 || y < 1 || y > 3)
? ? return 0;
? ? if(a[1] >= 7 || a[2] >= 7 || a[3] >= 7 || a[4] >= 7)
? ? return 0;
? ? if(((1 << aa) & DAY[t]) || ((1 << bb) & DAY[t]) || ((1 << cc) & DAY[t]) || ((1 << dd) & DAY[t]))
? ? return 0;
? ? if(mark[t][ys[aa]][a[1]][a[2]][a[3]][a[4]]) return 0;
? ? return 1;
}
void DFS(int t ,int x ,int y ,int a[])
{
? ? if(t == T) OK = 1;
? ? if(OK) return ;
? ? int b[5];
? ? for(int i = 0 ;i < 9 ;i ++)
? ? {
? ? ? ? if(t == 0 && i) break;
? ? ? ? int nowt = t + 1;
? ? ? ? int nowx = x + dir[i][0];
? ? ? ? int nowy = y + dir[i][1];
? ? ? ? for(int j = 1 ;j <= 4 ;j ++)
? ? ? ? b[j] = a[j] + 1;
? ? ? ? if(nowx == 1 && nowy == 1) b[1] = 0;
? ? ? ? if(nowx == 1 && nowy == 3) b[2] = 0;
? ? ? ? if(nowx == 3 && nowy == 1) b[3] = 0;
? ? ? ? if(nowx == 3 && nowy == 3) b[4] = 0;
? ? ? ? if(ok(nowt ,nowx ,nowy ,b))
? ? ? ? {
? ? ? ? ? ? mark[nowt][ys[(nowx-1)*4+nowy]][b[1]][b[2]][b[3]][b[4]] = 1;
? ? ? ? ? ? DFS(nowt ,nowx ,nowy ,b);
? ? ? ? }
? ? }
? ? return ;
}
int main ()
{
? ? int i ,j;
? ? while(~scanf("%d" ,&T) && T)
? ? {
? ? ? ? memset(DAY ,0 ,sizeof(DAY));
? ? ? ? for(i = 1 ;i <= T ;i ++)
? ? ? ? {
? ? ? ? ? ? int tmp;
? ? ? ? ? ? for(j = 1 ;j <= 16 ;j ++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? scanf("%d" ,&tmp);
? ? ? ? ? ? ? ? DAY[i] = DAY[i] | (tmp << j);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? memset(mark ,0 ,sizeof(mark));
? ? ? ? OK = 0;
? ? ? ? int a[5];
? ? ? ? a[1] = a[2] = a[3] = a[4] = 0;
? ? ? ? DFS(0 ,2 ,2 ,a);
? ? ? ? printf("%d\n" ,OK);
? ? }
? ? return 0;
}
總結
以上是生活随笔為你收集整理的POJ2044 深搜+剪枝(云彩下雨)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ1719行列匹配
- 下一篇: POJ2060最小路径覆盖