2021-01-31

导弹防御系统【dfs】

题目链接
引言(请忽略):今天是小白ACM集训的日子,然后数据结构实在是太难了,真是学不动了,然后就只能无助地去复习以前的题了,记得以前在SDUT程设二里面有一道题叫最少拦截系统,它是一道贪心,我个人觉得最长上升子序列的思路跟它有些相似。我们先分析一下这道题,引入一下这道最少拦截系统的题干和代码:
最少拦截系统
Description
提炼一下题干,就是飞来一些导弹,需要用炮弹系统依次拦截,它的第一发炮弹能够到达任意的高度,但以后每一发炮弹都不能超过前一发的高度.比如如果飞来一颗比之前所有导弹都高的导弹,你只能增加一个拦截系统。求拦截系统个数
Input
输入包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)

Output
对应输出拦截所有导弹最少要配备多少套这种导弹拦截系统.

Sample
Input
8 389 207 155 300 299 170 158 65
Output
2


#include 
#include 
int a,i;//用来保存导弹的高度 
int b[100000];//用来保存拦截系统能够拦截的高度 
int main()
{
     
	int n,cnt;//n代表总共的导弹的个数,cnt代表拦截系统的个数 
	while(scanf("%d",&n)!=EOF)
	{
     
		memset(b,0,sizeof(b));//b用来保存拦截系统能够拦截的最大高度 
		cnt=1;	
		for(i=0;i<n;i++)
		{
        
			scanf("%d",&a);
			if(i==0)
			  b[0]=a;
			int j;
			for(j=0;j<cnt;j++)//每次都用较小的高度来替换b中的数值 
			{
     
				if(b[j]>=a)//如果拦截系统能够拦截就拦截 
				{
     
					b[j]=a;
					break;
				}
			}
			if(j==cnt)//如果拦截系统都不能够拦截,就只能够再重新用一个拦截系统了 
			{
     
				b[cnt++]=a;
			}
		}
		printf("%d\n",cnt);
	}
	return 0;
}

这道题就是一道在已有的拦截系统里查找的题,因为每一套拦截系统在导弹飞来之时会判断能否拦截,并更新每一套拦截系统可拦截的最大高度。最后把计数的cnt输出即可,是一道贪心策略的题目,感觉难度还可以。
但是今天这道题目可真是给我这个小白难坏了。

187. 导弹防御系统

描述
为了对抗附近恶意国家的威胁,R国更新了他们的导弹防御系统。

一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降

例如,一套系统先后拦截了高度为3和高度为4的两发导弹,那么接下来该系统就只能拦截高度大于4的导弹。

给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。

输入格式
输入包含多组测试用例。

对于每个测试用例,第一行包含整数n,表示来袭导弹数量。

第二行包含n个不同的整数,表示每个导弹的高度。

当输入测试用例n=0时,表示输入终止,且该用例无需处理。

输出格式
对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。

数据范围
1≤n≤50
输入样例

5
3 5 2 4 1
0 

输出样例

2

样例解释
对于给出样例,最少需要两套防御系统。

一套击落高度为3,4的导弹,另一套击落高度为5,2,1的导弹。
题解:这道题网上大佬的解析很多,我作为小白也没有什么太深入的理解,可能有些错误认识。这里仅仅记录自己的一个学习过程。
首先我们看题,可以知道,有的导弹可以上升,也有的可以下降,所以我们不能像上道题那样,仅仅去记录多少组上升子序列的问题。
我想到的就是枚举然后去暴搜;
一个导弹的高度是应该放在递增的序列还是递减的序列,然后放到哪一个递增或者递减序列之中,就是要核心讨论的问题。
这个题对时间复杂度有要求,所以在搜索的过程要时刻想办法优化,在适当的地方去剪枝。
搜索

  1. 第一种搜索可能就是BFS,宽度优先,网上有大佬用这种方法,但介于内存储存的一些写法我不太明白,就不多说了;
  2. 第二种就DFS,就是我采用的方法,因为在剪枝的过程确实很好写,我就只能想到这个方法了,然后可以采用声明全局最小值记录和迭代深搜两种思路,时间复杂度都是n*2^n,所以我就用前者来写这个代码。

因为小白也不怎么会,可能注释写的比较繁琐,代码如下:

#include 
using namespace std;
const int N = 100;
int n;
int q[N];//q:依次飞来的导弹; 
int up[N],down[N];
int ans;  // 记录全局最小变量;

void dfs(int x,int su,int sd)  // x:当前枚举到哪个数,su:上升子序列的个数,sd:下降子序列的个数
{
     
    if(su + sd >= ans) return ; // 剪枝---此时不可能再更新了,return
    if(x==n) // 递归结束
    {
     
        ans = su + sd;//上升子序列和下降子序列个数和为答案 
        return ;
    }

    // 情况1:将当前数放到上升子序列中
    int k =0;//k用来遍历当前所有上升子序列的末尾值 
    while(k < su && up[k] >= q[x]) k++;  // 因为序列下标是从0开始的,所以是<
    int t=up[k]; // 为后边回溯标记当前遍历; 
    up[k] = q[x];//如果q[u]比末尾值大,则更新up数组; 
    if(k < su) dfs(x+1,su,sd); // 如果被更新---即不用另开一个上升子序列m
	//如果k>=su说明q[当前]小于所有上升子序列 
    else dfs(x+1,su+1,sd);//则su+1 (增加一个系统),继续深搜 
    up[k] = t; // 回溯之前的状态 
   //情况2与上同理; 
    // 情况2:将当前数放到下降子序列中
    k = 0;
    while(k < sd && down[k] <= q[x]) k ++;
    t = down[k];
    down[k] = q[x];
    if(k < sd) dfs(x+1,su,sd);
    else dfs(x+1,su,sd+1);
    down[k] = t;
}
int main()
{
     
    while(cin >> n&& n)
    {
     
        for(int i=0;i<n;i++)
		  cin >> q[i];
        ans=n;
        dfs(0,0,0);
        cout<<ans<<endl;
    }

    return 0;
}

这个题我觉得对我来说是挺难的,也借鉴了许多网上大佬的思路,仅用来记录自己的学习过程吧。
新手上路,多多包涵。

你可能感兴趣的:(笔记,算法,dfs,c++,c#,程序设计)