DFS專題

D - 連連看
Time Limit:10000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

“連連看”相信很多人都玩過。沒玩過也沒關係,下面我給大家介紹一下遊戲規則:在一個棋盤中,放了很多的棋子。如果某兩個相同的棋子,可以通過一條線連起來(這條線不能經過其它棋子),而且線的轉折次數不超過兩次,那麼這兩個棋子就可以在棋盤上消去。規定連線不能從棋盤外圍繞過。 
玩家鼠標先後點擊兩塊棋子,試圖將他們消去,然後遊戲的後臺判斷這兩個方格能不能消去。現在你的任務就是寫這個後臺程序。 

Input

輸入數據有多組。每組數據的第一行有兩個正整數n,m(0<n<=1000,0<m<1000),分別表示棋盤的行數與列數。在接下來的n行中,每行有m個非負整數描述棋盤的方格分佈。0表示這個位置沒有棋子,正整數表示棋子的類型。接下來的一行是一個正整數q(0<q<50),表示下面有q次詢問。在接下來的q行裏,每行有四個正整數x1,y1,x2,y2,表示詢問第x1行y1列的棋子與第x2行y2列的棋子能不能消去。n=0,m=0時,輸入結束。 
注意:詢問之間無先後關係,都是針對當前狀態的! 

Output

每一組輸入數據對應一行輸出。如果能消去則輸出"YES",不能則輸出"NO"。 

Sample Input

3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0

Sample Output

YES
NO
NO
NO
NO
YES


吐槽一下出題人文字表達能力,已在原文做修改。


代碼:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<stack>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

#define MaxSize 1005
#define inf 0x3f3f3f3f

int n,m,flag;
int mp[MaxSize][MaxSize],book[MaxSize][MaxSize];
int d[4][2]= {0,1,1,0,0,-1,-1,0};//右,下,左,上

void dfs(int x, int y, int cnt, int dir,int tx, int ty)
{

  if(x==tx&&y==ty)
    {
      flag=1;
      return;
    }

  if(x<0 || x>=n || y<0 ||y>=m||book[x][y]==1||mp[x][y]!=0) return;


///////////////////開始剪枝///////////////

  if(cnt>2) return;
  /*我最開始想的是,能走到這一步,說明前面說明沒找到目標點,
  如果此時的轉彎次數大於等於2,再怎麼走也不能在轉彎2次內到目標點了。
  纔怪咧!這又不是step,如果當前已經轉彎兩次了,之後一直按這最後一次的方向走,走到目標點也是可以的呀*/

  if(cnt==2)//如果已經轉兩次彎了,那麼目標點必定只能在當前點方向的前方。
    {
      if(dir == 0)
        {
          if(!(x == tx && ty > y)) return;
        }

      else if(dir == 2)
        {
          if(!(x == tx && ty < y)) return;
        }

      else if(dir == 1)
        {
          if(!(y == ty && tx > x)) return;
        }

      else if(dir == 3)
        {
          if(!(y == ty && tx < x)) return;
        }
    }
///////////////////剪枝結束///////////////


  book[x][y]=1;

  for(int i=0; i<4; i++)
    {
      if(dir==i)
        dfs(x+d[i][0],y+d[i][1],cnt,dir,tx,ty);
      else
        dfs(x+d[i][0],y+d[i][1],cnt+1,i,tx,ty);
    }

  book[x][y]=0;
}

int main()
{
  int x,y,tx,ty,q;
  while(~scanf("%d%d",&n,&m))
    {
      if(n==0&&m==0) break;

      for(int i = 0; i < n; i++)
        {
          for(int j = 0; j < m; j++)
            {
              scanf("%d",&mp[i][j]);
            }
        }

      scanf("%d",&q);

      while(q--)
        {
          scanf("%d%d%d%d",&x,&y,&tx,&ty);
          x--,y--,tx--,ty--;

          if(mp[x][y]!=mp[tx][ty]||mp[x][y]==0||(x==tx&&y==ty)) printf("NO\n");
          //注意如果是同一個點肯定是不能消去的【避坑之重複數據】

          else
            {
              flag=0;
              memset(book,0,sizeof(book));
              book[x][y]=1;

              for(int i=0; i<4; i++)
                {
                  dfs(x+d[i][0],y+d[i][1],0,i,tx,ty);
                  if(flag) break;//這裏也能剪枝,剪的還是大枝。我真機智。
                }

              if(flag) printf("YES\n");
              else printf("NO\n");
            }
        }
    }
  return 0;
}//FROM CJZ


這道題其實不能從外面走還要簡單一點,要是能從外面走的話,就在地圖外面加一圈0即可。


思考:

1、在進入dfs判斷的時候,我一開始是這樣寫的:

 if(x<0 || x>=n || y<0 ||y>=m||book[x][y]==1||(mp[x][y]!=0&&(x!=tx&&y!=ty))) return;

  if(x==tx&&y==ty)
    {
      flag=1;
      return;
    }

因爲我想的是我有可能此時走到了目標點,而這個點是非0的,所以這裏要留出個條件給這種情況。然而這樣的話,出現下面這中情況我的代碼就不對了:

5 5
1 2 1 2 1
0 0 0 2 0
0 0 0 0 0
0 0 0 0 0
1 1 1 1 1
1
1 4 1 2


因爲有可能路徑中存在和查詢點一樣的點,把路徑擋住。所以沒有到達最後目標點之前,我們都不能允許路徑中出現非0點。所以應該改成這樣:

  if(x==tx&&y==ty)
    {
      flag=1;
      return;
    }

  if(x<0 || x>=n || y<0 ||y>=m||book[x][y]==1||mp[x][y]!=0) return;


2、下面我們來說說這個結構:

void dfs(int x, int y, int cnt, int dir,int tx, int ty)
{
    
    
    .
    .
    .
    .

 判斷&剪枝
    
    .
    .
    .
    .
    
    
  book[x][y]=1;

  for(int i=0; i<4; i++)
    {
      if(dir==i)
        dfs(x+d[i][0],y+d[i][1],cnt,dir,tx,ty);
      else
        dfs(x+d[i][0],y+d[i][1],cnt+1,i,tx,ty);
    }

  book[x][y]=0;
}

對於dfs而言,每一輪return之前,都出是走的一條路徑。每一輪中,路徑當中的所走過的每一個點都應該進行標記,來防止“貪吃蛇的自己咬到自己”的情況,即:走過的點不能重複走。

而對於這個結構而言,它先對當前點進行標記,然後對當前點進行擴展dfs。對於當前點,對由它爲“起點”擴展出來的一堆堆點來說,這個當前點是被標記的,這很講理嘛(自己想一想)。以這個點所有擴展出來的dfs都結束之後,再取消它的標記,結束以當前點爲起始點的dfs路徑。然後return到它的上一個點,上一個點又繼續進行其他方向的dfs擴展。這種結構真是神奇,好好體會。


3、

void dfs(int x, int y, int cnt, int dir,int tx, int ty)

在bfs的時候,我們通常把點的其他屬性(如:步數,時間)用結構體來存,這樣在擴展的時候是沒有什麼問題的。而對於dfs,沒有了bfs的隊列節點儲存結構,這些附加屬性就可以寫在函數的形參裏面,這樣擴展的時候這些屬性就能照樣進行步進了。






E - DFS
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

A DFS(digital factorial sum) number is found by summing the factorial of every digit of a positive integer. 

For example ,consider the positive integer 145 = 1!+4!+5!, so it's a DFS number. 

Now you should find out all the DFS numbers in the range of int( [1, 2147483647] ). 

There is no input for this problem. Output all the DFS numbers in increasing order. The first 2 lines of the output are shown below.
 

Input

no input
 

Output

Output all the DFS number in increasing order.
 

Sample Output

1 2 ......

講道理,這道題和dfs沒一點關係。不過可以練習一下怎麼打表。

打表代碼:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<stack>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

#define MaxSize 45
#define inf 0x3f3f3f3f

int jc[100];


int main()
{
  jc[0]=1;

  int sum;
  int cnt ;
  int x ;

  sum=1;
  for(int i = 1; i <= 9; i++)
    {
      sum*=i;
      jc[i]=sum;
    }

  FILE *fp=fopen("dfs_num.txt","w+");

  for(int i=1; i<=2147483647; i++)
    {

      sum=0;
      cnt = 0;
      x = i;

      while(x != 0)
        {
          sum+=jc[x%10];

          x/=10;
        }

      if(sum==i)
      {
          fprintf(fp,"%d\\n",i);
          //fflush(fp);
      }

    }

  fclose(fp);


  return 0;
}//FROM CJZ


這個程序在運行的過程中,我發現txt裏一直沒有出現數據,但是我們知道1,2都是符合要求的數據,在循環的開頭的時候就應該會輸出纔對。去問了學長才知道,有個東西叫緩衝區,數據都在緩衝區裏面。正常情況是:緩衝區接受數據->緩衝區滿->打印數據,同時緩衝區清空->繼續接受數據->…………因爲這裏的數據量很少(一共都才4個),緩衝區一直沒滿就不會打印結果,只有程序結束之後纔會把緩衝區裏的數據一起輸出。如果需要實時輸出數據的話可以用fflush(FILE*)函數,強行清空緩衝區,打印數據。不過對於我們來說,這裏我們都要等到程序結束拿到所有數據,所以用不用都一樣。


最後出來4個數據1,2,145,40585,直接打表輸出就可以了。

提交代碼;

#include<stdio.h>
int main()
{
 printf("1\n2\n145\n40585\n");
 return 0;
}







F - 棋盤問題
Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%lld & %llu

Description

在一個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請編程求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案C。

Input

輸入含有多組測試數據。 
每組數據的第一行是兩個正整數,n k,用一個空格隔開,表示了將在一個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n 
當爲-1 -1時表示輸入結束。 
隨後的n行描述了棋盤的形狀:每行有n個字符,其中 # 表示棋盤區域, . 表示空白區域(數據保證不出現多餘的空白行或者空白列)。 

Output

對於每一組數據,給出一行輸出,輸出擺放的方案數目C (數據保證C<2^31)。

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1



代碼:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<vector>
#include<queue>
#include<stack>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

#define MaxSize 10
#define inf 0x3f3f3f3f
#define LL long long int

int n,k,ans;
int book[MaxSize];
char mp[MaxSize][MaxSize];

void dfs(int r, int cnt)
{
  if(cnt == k)
    {
      ans++;
      return;
    }

  if(r == n) return;
//上面這兩個判斷的順序一定要這樣寫*
  for(int i=0; i<n; i++)
    {
      if( mp[r][i]=='.' || book[i]==1) continue;

      book[i]=1;//說明當前i可以選,則標記。然後cnt+1,繼續找下一行

      /**想一想如果是在最後一行(即n-1行)找到的最後一個cnt的情況,
      這就決定了上面的return順序。如果換一個順序的話,這種情況下ans還沒++就return了,
      所以不行*/
      dfs(r+1,cnt+1);

      book[i]=0;
    }
    /*並不是每一行都能找到滿足條件的棋盤位置的,
    如果就這樣結束了的話,這次for沒找到位置,沒進行往後的dfs,
    那麼後面就斷了。所以不難想到,沒找到的話,我們應該有個跳行語句。
    而且我們知道,在選位置的時候不一定必須一行一行挨着選,
    我們可以跳過某行或者某幾行(只要是滿足條件的)。如果我們就只寫個for就完了的話,
    這明顯就不能實現跳行了,所以我們讓每個點多一個可以選擇跳行的語句。
    從以上兩點來看,無論哪種情況,都應該在for結束之後有跳行的語句,即對於每一個點,
    它要進行兩種選擇,一是在下一行裏面選擇合適的位置,二是跳過下一行,進入再下一行去選。
    這裏舉個例子,比如我們要隔兩行再開始選,
    那麼這裏我們會經歷dfs(r+1,cnt)進入下一行,
    下一行的時候又會經歷dfs((r+1)+1,cnt),所以一定會囊括所有跳行情況*/
    dfs(r+1,cnt);
}

int main()
{
  while(~scanf("%d%d",&n,&k))
    {
      if(n==-1 && k==-1) break;

      getchar();
      for(int i=0; i<n; i++)
        {
          for(int j=0; j<n; j++)
            {
              scanf("%c",&mp[i][j]);
            }

          getchar();
        }

      memset(book,0,sizeof(book));
      ans=0;

      dfs(0,0);

      printf("%d\n",ans);
    }
  return 0;
}//FROM CJZ






發佈了47 篇原創文章 · 獲贊 43 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章