题解 P1020 【导弹拦截】
深搜+贪心
看到各位dalao各种dp和什么什么序列,身为一名准初一蒟蒻的我深深地自愧不如,在此提供一种比较新的思路(逃)。
第一问我用的是dfs(话说记搜和dp差不多啊......dp党不要喷我),第二问我用的是贪心+模拟,感觉好理解一点。
第一问很简单,对于每一枚导弹,如果可以拦截,有两种选择:拦截或不拦截,若不可以拦截,就只有不拦截一种选择了。加个记忆化,很好写啊!(好吧,本质上还是最长不上升子序列,只不过换了一种说法)。
第二问也不难,先开一台系统,看是否可以拦截接下来这枚导弹,如果能,就更新高度;如果不能,就新开一台系统。如果有多台系统可以拦截,选高度最低的那台就可以了。(比dp好理解一点吧?)
啥都不说了,上代码吧!
#include<iostream>
#include<fstream>
#include<algorithm>
#include<string>
#include<sstream>
using namespace std;
int w=0,e=0,a[105],d[105],f[105][30005];
int dfs(int x,int y)//dfs,表示在第x枚导弹来时,目前能拦截的导弹高度为y时的最优方案
{
if(f[x][y]!=-1) return f[x][y];//记忆化,剪枝
if(x==w) return 0;//如果到最后一枚导弹了,结束搜索
//如果这一枚导弹可以被拦截,就有拦截和不拦截两种选择,取最大值
//否则,只有不拦截这一种选择
if(a[x+1]<=y) f[x][y]=max(dfs(x+1,a[x+1])+1,dfs(x+1,y));
else f[x][y]=dfs(x+1,y);
return f[x][y];//返回
}
int main()
{
//此题没有数据个数,搞到我要文件读写......
//freopen("L1020-导弹拦截.in","r",stdin);
for(int i=0;i<105;i++)//初始化一下,养成良好习惯,能有效防止莫名的TLE哦~
for(int j=0;j<30005;j++) f[i][j]=-1;
//P.S:有些题用深搜不初始化会T是因为一但出现结果为0的情况你就挂了
while(cin>>a[++w]);//读入
w--;//这里-1是因为最后读了一个EOF,要忽略掉
cout<<dfs(0,30005)<<endl;//深搜并输出(初始高度肯定要高高的啦)
//-------------第二问开始-------------
d[++e]=30005;//第一台导弹系统初始高度也要高高的
for(int i=1;i<=w;i++)
{
int Min=30005,f=0;
//查找能拦截此枚导弹的,高度最低的系统
for(int j=1;j<=e;j++)
if(d[j]>=a[i]&&d[j]<=Min)
{
Min=d[j];
f=j;
}
if(f!=0) d[f]=a[i];//如果找到了,更新那台系统的高度
else d[++e]=a[i];//若找不到,再找一台
}
cout<<e;//输出
return 0;
}