洛谷P1158 [NOIP2010 普及组] 导弹拦截
因为只有两台装备,故把使用代价分为两部分,一部分是每个导弹到设备1的距离(降序处理减少时间复杂度),另一部分则是到设备2的距离(贪心考虑,每次循环时取max),两部分加起来就是本题答案。
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,xa,xb,ya,yb;
struct hh {
int d1,d2;
bool operator <(const hh &b) const {
return d1>b.d1;
}
}a[N];
int main() {
xa=read();ya=read();xb=read();yb=read();
n=read();
for(R i=1;i<=n;++i) {
int x=read(),y=read();
a[i].d1=(x-xa)*(x-xa)+(y-ya)*(y-ya);//到导弹1的距离
a[i].d2=(x-xb)*(x-xb)+(y-yb)*(y-yb);//到导弹2的距离
}
sort(a+1,a+1+n);//将到导弹1的距离按降序排
int ans=a[1].d1,d=0;
for(R i=2;i<=n;++i) {
d=max(d,a[i-1].d2);
ans=min(ans,d+a[i].d1);
}
printf("%d
",ans);
return 0;
}
洛谷P1020 [NOIP1999 普及组] 导弹拦截
关于为什么这道题是求一个最长不上升子序列和一个最长上升子序列:DP-导弹拦截
一旦对降序序列使用lower_bound,就会出现神奇的错误,具体原因可以看这篇:
https://blog.csdn.net/qq1337715208/article/details/81072709
当然比较器默认也是"<"
如果要在一个下降序列里寻找一个小于x的数呢?
根据我们之前说的,lower_bound只能对上升序列使用,那我假装下降序列是个上升序列就行了嘛~
(lower_bound:你当我傻吗)(w1049:没错我就当你傻)
只需要把比较器改成">":
lower_bound(a + 1, a + 1 + n, x, cmp);
同时需要写一个函数cmp:
bool cmp(const int& a,const int& b){return a > b;}
当然,你也可以这样:
lower_bound(a + 1, a + 1 + n, x, greater <int> () );
这里的**greater<int>()**就是c++友情提供的方便的大于函数,这样就不用自己动手写一个cmp函数了(其实就是懒)
它们的实现方式是二分查找 ,存在的意义就是让我们写代码更方便地偷懒(一行lower_bound比写二分查找方便多了)
但是,但是!!!B[ ] 中的序列并不一定是正确的最长上升子序列。
B序列并不一定表示最长上升子序列,它只表示相应最长子序列长度的排好序的最小序列。这有什么用呢?我们最后一步3替换5并没有增加最长子序列的长度,而这一步的意义,在于记录最小序列,代表了一种“最可能性”,只是此种算法为计算LIS而进行的一种替换。
//STL写法
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,a[N],low1[N],low2[N],ans1,ans2;
int main() {
int x;
while(~scanf("%d",&x)) a[++n]=x;
ans1=ans2=1;
low1[1]=low2[1]=a[1];//Attention!
for(R i=2;i<=n;++i) {
//最多拦截多少导弹:最长不上升子序列
if(a[i]<=low1[ans1]) low1[++ans1]=a[i];
else {
int t=upper_bound(low1+1,low1+1+ans1,a[i],greater<int>())-low1;//小于a[i]的最大的数,Attention!
low1[t]=a[i];
}
//最长上升子序列
if(a[i]>low2[ans2]) low2[++ans2]=a[i];
else {
int t=lower_bound(low2+1,low2+1+ans2,a[i])-low2;//大于等于a[i]的最小的数
low2[t]=a[i];
}
}
printf("%d
%d
",ans1,ans2);
return 0;
}
据说这是个二维偏序的题目,还可以用树状数组写一写
//树状数组写法
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,ans,l[N];
struct hh {
int num,val;
}a[N];
bool cmp1(hh a,hh b) {return a.val==b.val?a.num<b.num:a.val>b.val;}
bool cmp2(hh a,hh b) {return a.val==b.val?a.num>b.num:a.val<b.val;}//Attention!此处num要升序排列,这样统计最长上升子序列的时候就不会重复统计val相同而num不同的数了
int lowbit(int x) {return x&(-x);}
void modify(int x,int y) {for(;x<=n;x+=lowbit(x)) l[x]=max(l[x],y);}
int query(int x) {
int res=0;
for(;x;x-=lowbit(x)) res=max(res,l[x]);
return res;//别忘了return(吸取教训
}
int main() {
int x;
while(~scanf("%d",&x)) a[++n].val=x,a[n].num=n;
sort(a+1,a+1+n,cmp1);//最长不上升子序列,降序排列
for(R i=1;i<=n;++i) {
int t=query(a[i].num)+1;//查询编号小于a[i].num的最长不上升子序列的长度
modify(a[i].num,t);
ans=max(ans,t);
}
printf("%d
",ans);
memset(l,0,sizeof l);
ans=0;
sort(a+1,a+1+n,cmp2);//最长上升子序列,升序排列
for(R i=1;i<=n;++i) {
int t=query(a[i].num)+1;//查询编号小于a[i].num的最长上升子序列的长度
modify(a[i].num,t);
ans=max(ans,t);
}
printf("%d
",ans);
return 0;
}
还有一点需要注意:树状数组求LIS不去重的话就变成了最长不下降子序列了。
洛谷P3903 导弹拦截III
拦截的导弹高度先递减后递增,故求出波峰和波谷的数量和即可
代码还是很巧妙的
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,ans=1,flag=0,last;//0递增,1递减
int main() {
n=read();
for(R i=1;i<=n;++i) {
int x=read();
if(flag^(x<last)) ans++,flag=x<last;
last=x;
}
printf("%d
",ans);
return 0;
}
洛谷P1091 [NOIP2004 提高组] 合唱队形
这题和导弹没啥关系,但是涉及到了LIS,顺便放上来吧
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,ans,f1[N],f2[N],a[N];
//f1[i]:以i结尾的上升序列的长度
//f2[i]:以i开头的下降序列的长度
int main() {
n=read();
for(R i=1;i<=n;++i) a[i]=read(),f1[i]=f2[i]=1;//Attention!初始化!
for(R i=1;i<=n;++i)
for(R j=1;j<i;++j)
if(a[j]<a[i]) f1[i]=max(f1[i],f1[j]+1);
for(R i=n;i;--i)
for(R j=n;j>i;--j)
if(a[j]<a[i]) f2[i]=max(f2[i],f2[j]+1);
for(R i=1;i<=n;++i) ans=max(ans,f1[i]+f2[i]-1);
printf("%d",n-ans);
return 0;
}
那顺便把LCS也放上来吧
洛谷P1439 【模板】最长公共子序列
关于为什么这道题简化成了最长上升子序列:题解 P1439 【【模板】最长公共子序列】
代码很简单:有点像离散化(映射),其他的都是LIS的基本操作
//STL写法
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,ans,b[N],low[N],a[N];
int main() {
n=read();
for(R i=1;i<=n;++i) a[i]=read(),b[a[i]]=i;
for(R i=1;i<=n;++i) {
a[i]=read();
a[i]=b[a[i]];
}
low[1]=a[1];ans=1;
//求最长上升子序列
for(R i=2;i<=n;++i) {
if(a[i]>low[ans]) low[++ans]=a[i];
else {
int t=lower_bound(low+1,low+1+ans,a[i])-low;
low[t]=a[i];
}
}
printf("%d",ans);
return 0;
}
//线段树写法
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;
IL int read() {
int f=1;
char ch;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
int res=ch-'0';
while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
return res*f;
}
int n,ans,b[N],l[N];
struct hh {
int val,num;
bool operator <(const hh &b) const {
return val==b.val?num<b.num:val<b.val;
}
}a[N];
int lowbit(int x) {return x&(-x);}
int query(int x) {
int res=0;
for(;x;x-=lowbit(x)) res=max(res,l[x]);
return res;
}
void modify(int x,int y) {for(;x<=n;x+=lowbit(x)) l[x]=max(l[x],y);}
int main() {
n=read();
for(R i=1;i<=n;++i) a[i].val=read(),b[a[i].val]=i;
for(R i=1;i<=n;++i) {
a[i].val=read();
a[i].val=b[a[i].val];
a[i].num=i;
}
sort(a+1,a+1+n);
for(R i=1;i<=n;++i) {
int t=query(a[i].num)+1;
modify(a[i].num,t);
ans=max(ans,t);
}
printf("%d",ans);
return 0;
}