一、题目解析
我写的可能有点乱,但是是一步步实现,中间可能会产生一些疑问,但整体应该是正确的。
题目说了老长,实际上意思就是让我们分析一下,是否存在让所有的飞机降落成功的一个方案。
但是有一个条件,那就是只有上一架飞机着陆之后,这一架飞机才能开始着陆。
我们这里沿用一下贪心的思路,当上一架飞机落地之后,我当前的这个飞机马上开始着陆(但必须在可以开始着陆之后)。局部最优->全局最优,无明显反例,所以认为这个策略是正确的。
二、算法分析
我们先从输入输出开始分析,首先我们要输入的是t次询问,每次询问都要输入n架飞机的各种状态,比如:飞机的可开始降落的时间,还能还能保持飞行的时间,以及降落所需要的时间。
1.分析输入
我们可以先写出以下的代码:
#include <iostream>
using namespace std;
int n,q;
const int N = 20;
int T[N],D[N],L[N];
int main()
{
cin>>q;
while(q--)
{
scanf("%d",&n);
for(int i = 0;i<n;i++) scanf("%d%d%d",&T[i],&D[i],&L[i]);
}
return 0;
}
2.确定使用的算法
然后我们再来考虑,要用什么方法,目测本题的数据范围只有10,请看下边这个表——
由表启发,我们不难想到可以使用dfs碰一下运气,万一可以呢?
1)分析一下dfs的(部分)参数和返回值
再看一下输出——问我们是否能够成功降落?输出YES或者NO
那么我们再写dfs的时候,直接让其返回真假即可(确定了dfs的返回值)。
然后再来确定一下dfs函数的参数。
在暴搜的时候,也就是暴力枚举每一个飞机,那我们肯定得知道自己枚举到哪一个飞机了,以及哪一个飞机被自己枚举过了。
那我们不妨把我们当前枚举到得飞机的编号当作函数的参数(也就是他们的下标,我们从0开始)。而在一开始,所有的飞机都给他们一个状态,那就是未降落(flase),枚举的时候,如果他降落了,我们就把他的状态改为true,回溯的时候在改为false,这就是我们要开一个bool数组state的原因。
于是我们结合一下输出和以上我们的分析,补充一下我们的代码。
#include <iostream>
using namespace std;
int n,q;
const int N = 20;
int T[N],D[N],L[N];
bool state[N];
bool dfs(int u)
{
}
int main()
{
cin>>q;
while(q--)
{
scanf("%d",&n);
for(int i = 0;i<n;i++) scanf("%d%d%d",&T[i],&D[i],&L[i]);
if(dfs(0))printf("YES\n");
else printf("NO\n");
}
return 0;
}
2)dfs函数的实现
好,铺垫工作差不多结束了,现在进入我们的核心内容,dfs函数的编写。
这里的dfs直接用朴素递归形式,分为一下三个步骤:
1.递归的出口
2.函数的主体
3.函数的返回值
Ⅰ.函数的出口
这里我们有两种策略,第一种就是把所有的飞机枚举完之后再去判断,第二种策略是在枚举的过程中,只枚举那些可能成功的方案(也就是用了剪枝的思想)。
这里我们选取的是第二章策略,因为他快。
我先直接给出递归的出口,等我们完成整个函数的主体之后,就会对这个递归出口有更深入的理解。
bool dfs(int u)
{
if(u == n)//这里的n是一个全局变量,表示的是飞机的数量,但由于我们的飞机的编号是从0开始,
{//所以当我们枚举到的飞机的编号等于飞机的数量的时候,绝对是已经全部枚举完了,并且这个方案一定是合理的
return true;
}
}
相信读者可能会对这个递归出口产生一些质疑,但是请往下看。
Ⅱ.函数的主体
我们用for循环去暴力枚举所有可能的情况——
bool dfs(int u)
{
if(u == n)return true;
for(int i = 0;i<n;i++)
{
if(!state[i])//表示这个位置的飞机还没有降落
{
}
}
}
但是我们发现了一个问题,那就是我们缺少条件,我们在题目解析的时候分析出,一架飞机落地的时候,一架飞机就要开始落地,这样的策略才是最优的。
但是我们又怎么知道,上一架飞机的着陆时间的,所以我们分析出我们是缺少了参数。
我们把它设为time,表示上一架飞机的着陆时间。
修改后的代码如下:
bool dfs(int u,int time)
{
if(u == n)return true;
for(int i = 0;i<n;i++)
{
if(!state[i]&&T[i]+D[i]>=time)//保证这个飞机还没有降落,并且它可以撑到这个time时候
{
state[i] = true;
if(dfs(u+1,max(time,T[i])+L[i])) return true;//取最大的操作是因为可能上一架飞机已经着陆了,但是这一架飞机还不没到开始降落的世家。
state[i] = false;
}
}
return false;
}
注释已经很详细了,这里不过多赘述。
Ⅲ.函数的返回值
函数的返回值是真假,这个没有问题,继续沿用即可。
三、最终代码呈现
但是我们共用的同一个state数组,所以每一次使用前,都要将其用memset重置一下(头文件是<cstring>)。
#include <iostream>
#include <cstring>
using namespace std;
int n,q;
const int N = 20;
int T[N],D[N],L[N];
bool state[N];//表示这个飞机降落了没有
bool dfs(int u,int time)
{
if(u == n)return true;
for(int i = 0;i<n;i++)
{
if(!state[i]&&T[i]+D[i]>=time)//保证这个飞机还没有降落,并且它可以撑到这个time时候
{
state[i] = true;
if(dfs(u+1,max(time,T[i])+L[i])) return true;
state[i] = false;
}
}
return false;
}
int main()
{
// 请在此输入您的代码
cin>>q;
while(q--)
{
scanf("%d",&n);
for(int i = 0;i<n;i++)scanf("%d%d%d",&T[i],&D[i],&L[i]);
memset(state,0,sizeof state);//重置状态
if(dfs(0,0))printf("YES\n");
else printf("NO\n");
}
return 0;
}