【题解&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| RA+CB

当然,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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值