Description
玩家鼠標先後點擊兩塊棋子,試圖將他們消去,然後遊戲的後臺判斷這兩個方格能不能消去。現在你的任務就是寫這個後臺程序。
Input
注意:詢問之間無先後關係,都是針對當前狀態的!
Output
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的隊列節點儲存結構,這些附加屬性就可以寫在函數的形參裏面,這樣擴展的時候這些屬性就能照樣進行步進了。
Description
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
Output
Sample Output
講道理,這道題和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
最後出來4個數據1,2,145,40585,直接打表輸出就可以了。
提交代碼;
#include<stdio.h>
int main()
{
printf("1\n2\n145\n40585\n");
return 0;
}
Description
Input
每組數據的第一行是兩個正整數,n k,用一個空格隔開,表示了將在一個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n
當爲-1 -1時表示輸入結束。
隨後的n行描述了棋盤的形狀:每行有n個字符,其中 # 表示棋盤區域, . 表示空白區域(數據保證不出現多餘的空白行或者空白列)。
Output
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