1. 程式人生 > >【題解&7海亮集訓】複賽前夕——day 2 專項練習 搜尋

【題解&7海亮集訓】複賽前夕——day 2 專項練習 搜尋


一、分數字


題目描述
現有N件不可區分的物品,將它們分成10份,要求每份在1~3件之間,問有多少種方案,並按字典序輸出所有方案。


輸入格式
一個整數,表示N<=10000N<=10000。


輸出格式
第一行一個數M,表示方案數。
接下來M行每行10個整數,表示1種方案。


樣例資料
input
11


output
10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1


分析:兩種方法,一種是很乖的去dfs(一般人都不會這樣。),另一種方法就是十重迴圈暴力列舉,判斷最後的物品是否等於n即可
這裡給上第二種方法的程式碼:

#include<bits/stdc++.h>
using namespace std;
int n;
int len=0;
struct node{
    int aa,bb,cc,dd,ee,ff,gg,hh,ii,jj;
}m[10000001];
int main(){
	freopen("allot.in","r",stdin);
	freopen("allot.out","w",stdout);
    scanf("%d",&n);
    for (int a=1;a<=3;a++)
      for (int b=1;b<=3;b++)
        for (int c=1;c<=3;c++)
          for (int d=1;d<=3;d++)
            for (int e=1;e<=3;e++)
              for (int f=1;f<=3;f++)
                for (int g=1;g<=3;g++)
                  for (int h=1;h<=3;h++)
                    for (int i=1;i<=3;i++)
                      for (int j=1;j<=3;j++)
                        if (a+b+c+d+e+f+g+h+i+j==n)
						    m[++len]=(node){a,b,c,d,e,f,g,h,i,j};
	printf("%d\n",len);
	for (int i=1;i<=len;i++)
	  printf("%d %d %d %d %d %d %d %d %d %d\n",m[i].aa,m[i].bb,m[i].cc,m[i].dd,m[i].ee,m[i].ff,m[i].gg,m[i].hh,m[i].ii,m[i].jj);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

二、流星雨
題目描述
流星雨是美麗的,但是流星落下來也能砸死人的。

有一大片流星要在海亮教育園的操場落下,而小x恰好在操場數星星。小x面臨最大的問題不是浪漫,而是保住小命。

我們把海亮教育園的操場認為是座標系的第一象限(以樣例解釋的圖例為準)。小x現在位於座標系的原點。

現在有M顆流星會落在海亮教育園的操場上,其中第i顆流星會在時刻T_i砸在座標為(X_i, Y_i)的格子裡。流星的力量會將它所在的格子,以及周圍4個相鄰的格子都化為焦土,當然小x也無法再在這些格子上行走,這樣他會被燒傷。

小x從0時刻開始逃離,他只能上下左右移動,並且一個時刻只能移動一個格子,當然,這個格子必須是完好的。

現在小x想知道,最少經過多少時刻,他可以到達一個安全的格子。


輸入格式
一個正整數M 接下來M行,每行3個整數Xi,Yi和Ti。分別表示第i顆流星落下的座標和時間。保證所有座標都在第一象限。


輸出格式
一個整數,表示最少逃離時刻。如果逃離不了,那麼輸出-1,表示小x肯定被流星砸著了。


input
4
0 0 2
2 1 2
1 1 2
0 3 5


output
5


【樣例說明】

一共有4顆流星將墜落在操場,它們落地點的座標分別是(0, 0),(2, 1), (1, 1)以及(0, 3),時刻分別為2,2,2,5。

在t=5時的牧場,離小x最近的安全的格子是(3,0)——不過由於早在第二顆流星落地時,小x直接跑去(3,0)的路線就被封死了。離小x第二近的安全格子為(4,0),但它的情況也跟(3,0)一樣。再接下來的格子就是在(0,5)-(5,0)這條直線上。在這些格子中,(0,5),(1,4)以及(2,3)都能在5個單位時間內到達。


分析:這題是一道很明顯的BFS題(其實任何地圖版就最小值的都是BFS的模板題。。。),我們先用vis陣列預處理出當前格子是否有流星,初值為-1,表示沒流星。邊輸入邊處理,處理完之後vis[i][j]表示流星落到(i,j)位子上的最短時間
處理完陣列之後我們可以根據時間為單位進行BFS,最先走到安全區的時間即為最優時間,如果走不到,那就輸出-1

程式碼:

#include<bits/stdc++.h>
using namespace std;
int vis[1001][1001];
int n;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int maxx=0,maxy=0;
int t[1001][1001];
typedef pair < int , int > pii;
int ans=0;
bool f[1001][1001];
void boob(int xx,int yy,int zz){
    vis[xx][yy]=vis[xx][yy]>0?min(vis[xx][yy],zz):zz;
    for (int i=0;i<=3;i++){
	    int xxx=xx+dx[i],yyy=yy+dy[i];
	    if (xxx<0||yyy<0) continue;
	    vis[xxx][yyy]=vis[xxx][yyy]>0?min(vis[xxx][yyy],zz):zz;
	}
}
int main(){
	freopen("meteor.in","r",stdin);
	freopen("meteor.out","w",stdout);
    scanf("%d",&n);
    memset(vis,-1,sizeof(vis));
    for (int i=1;i<=n;i++){
	    int x,y,z;
	    scanf("%d %d %d",&x,&y,&z);
	    boob(x,y,z);
	}
	queue < pii > q;
	memset(t,50,sizeof(t));
	t[0][0]=0;
	q.push(make_pair(0,0));
	if (vis[0][0]==-1){cout<<0;return 0;}
	while (!q.empty()){
	    int xx=q.front().first,yy=q.front().second;
	    int len=q.size();
	    q.pop();
	    for (int i=0;i<=3;i++){
		    int xxx=xx+dx[i],yyy=yy+dy[i];
		    t[xxx][yyy]=min(t[xxx][yyy],t[xx][yy]+1);
		    if (xxx<0||yyy<0||f[xxx][yyy]) continue;
		    if (vis[xxx][yyy]!=-1&&vis[xxx][yyy]<=t[xxx][yyy]) continue;
		    if (vis[xxx][yyy]==-1) {printf("%d",t[xxx][yyy]);return 0;}
		    f[xxx][yyy]=1;
		    q.push(make_pair(xxx,yyy));
		}
//		if (len==q.size())break;
	}
	cout<<-1;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

三、安全回家
題目描述
Farmer John在森林裡迷路了,現在他急需要回家。

森林被分成了N*M的矩陣,每個單元格有一個字元來標示屬性,’.'表示空地,‘V’表示Fj所在的位置,‘J’表示Fj家的位置。在森林裡有很多危險的地方,危險的地方被標記為’+’。 Fj想要在回家的路上,讓自己距離危險的地方儘可能的遠,請你幫他設計一條線路,讓這條線路上的每個點距離 危險的地方 最近的距離 最大。

假設Fj的位置為 (R,C),某個危險點的位置為 (A,B)Fj距離危險點的距離為:

R A + C B |R-A| + |C-B|

當然,Fj必須要回家,也許他必須要經過危險的地方。


輸入格式
第一行兩個整數:N和M (1 ≤ N, M ≤ 500)
接下來N行,每行M個字元,範圍是:’.’, ‘+’, ‘V’, ‘J’.
資料保證肯定有’V’, ‘J’,且至少有一個’+’


輸出格式
一個整數,表示那個距離


input1
4 4
+…


V…J


output1
3


input2
4 5

.+++.
.+.+.
V+.J+


output2
0


分析:先做一遍BFS來預處理當前點距離危險點的最近距離,然後再來一遍DFS來更新每個點到危險點的距離即可

程式碼:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int dis[610][610];
int st,stt,en,enn;
typedef pair < int , int > pii;
queue < pii > q;
int a[1001][1010];
bool vis[1001][1001];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(int xx,int yy,int t){
    if (t<=a[xx][yy])return;
    a[xx][yy]=t;
    if (xx==en&&yy==enn)return;
    for (int i=0;i<4;i++){
	    int xxx=xx+dx[i],yyy=yy+dy[i];
	    if (xxx<1||xxx>n||yyy<1||yyy>m)continue;
	    dfs(xxx,yyy,min(t,dis[xxx][yyy]));
	}
}
int main(){
	freopen("vuk.in","r",stdin);
	freopen("vuk.out","w",stdout);
	scanf("%d %d",&n,&m);
	memset(dis,50,sizeof(dis));
	for (int i=1;i<=n;i++)
	  for (int j=1;j<=m;j++){
	      char ch;
	      ch=getchar();
	      while (ch!='.'&&ch!='V'&&ch!='+'&&ch!='J') ch=getchar();
		  if (ch=='V') st=i,stt=j;
	      if (ch=='J') en=i,enn=j;
	      if (ch=='+') q.push(make_pair(i,j)),dis[i][j]=0,vis[i][j]=1; 
	  }
	while (!q.empty()){
	    int xx=q.front().first,yy=q.front().second;
	    q.pop(); 
	    for (int i=0;i<4;i++){
		    int xxx=xx+dx[i],yyy=yy+dy[i];
		    if (xxx<1||xxx>n||yyy<1||yyy>m||vis[xxx][yyy]) continue;
		      vis[xxx][yyy]=1,dis[xxx][yyy]=dis[xx][yy]+1,q.push(make_pair(xxx,yyy));
		}
	}
	dfs(st,stt,dis[st][stt]);
//	for (int i=1;i<=n;i++){ 
//	  for (int j=1;j<=m;j++)
//	    cout<<dis[i][j]<<' ';
//	   cout<<endl;
//	}
    cout<<a[en][enn];
    fclose(stdin);
    fclose(stdout);
	return 0;
}

四、黑洞
題目描述
李宗澤的愛好是在週末進行物理學實驗,但事與願違,實驗將N個黑洞(2 <= N <= 12, N為even)具象化在了他的農場裡,每個都有明確的座標位置。

根據他的計算,李宗澤知道將會形成N/2對連線起來的黑洞。如果黑洞A和B被連成一對,那麼任何物體進入黑洞A,將會以進入黑洞A的方向從黑洞B中出來;進入黑洞B,也會以進入時的方向從黑洞A中出來。舉例來說,黑洞A在(0,0),黑洞B在(1,0),牛玉鑫從(1/2,0)開始向X軸正方向移動,進入黑洞B,從黑洞A中出來,將繼續向X軸正方向移動,再次進入黑洞B,被困在一個迴圈裡。

李宗澤知道每一個黑洞在他的農場上的具體座標,牛玉鑫只會向X軸正方向移動,但卻不知道牛玉鑫目前的位置。

請你幫助李宗澤計算共有多少種黑洞配對方法會使在不幸的位置的牛玉鑫陷入迴圈。


輸入格式
第一行:一個正整數N;
第二到N+1行:每行兩個整數X,Y描述一個黑洞的位置,每個座標在0…1,000,000,000內。


輸出格式
一個數,代表所有的會讓牛玉鑫陷入迴圈的黑洞配對方法數。


input
4
0 0
1 0
1 1
0 1

有4個黑洞,形成一個正方形的迴圈。
output
2


給這4個黑洞編號為1…4。如果將1和2相連,3和4相連,牛玉鑫從1和2之間或3和4之間出發時會陷入迴圈。相同的,如果連線1和3,2和4,牛玉鑫也會陷入迴圈。只有連線1和4,2和3,牛玉鑫從任何一個位置開始移動都不會陷入迴圈。


分析:用next陣列預處理距離第i個黑洞最近的黑洞的編號,然後用dfs列舉它所連線的那個黑洞,在用圖論SPFA的思想判斷是否出現環,如果出現,累加次數

程式碼:

#include<bits/stdc++.h>
using namespace std;
int pa[10001];
int n;
int next[100001];
int x[100001],y[100001];
inline bool checkround(){
    for (int i=1;i<=n;i++){
	    int now=i;
	    for (int j=1;j<=n;j++) now=next[pa[now]];
	    if (now)return 1;
	} 
	return 0;
}
int dfs(){
	int ans=0;
    int x=1;
    while (pa[x]) x++;
    if (x>n) return checkround();
    for (int i=x+1;i<=n;i++)
      if (!pa[i]){
	      pa[i]=x;
	      pa[x]=i;
	      ans+=dfs();
	      pa[i]=pa[x]=0;
	  }
	return ans;
}
int main(){
	freopen("aaaaa.in","r",stdin);
	freopen("aaaaa.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d %d",&x[i],&y[i]);
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        if (x[j]>x[i]&&y[j]==y[i])
          if (x[j]-x[i]<x[next[i]]-x[i]||next[i]==0)
            next[i]=j;
    printf("%d",dfs());
    fclose(stdin);
    fclose(stdout);
    return 0;
}