返回顶部

线性DP

线性DP

线性DP要解决的问题多和最长上升子序列有关,而题目给出的数据多未排序,很难观察出要求的答案。题目中每个元素多有两个变量,可以尝试着将其中一个变量按照升序或者降序排好序,这样往往方便观察,容易找到思路。

一.最长上升子序列

  #include<bits/stdc++.h>
  using namespace std;
  const int N=1005;
  int f[N],h[N],i,p[N],n,j;
  void out(int x){
  	if(x==0)return;
  	out(p[x]);
  	cout<<h[x]<<' ';
  	return;
  }
  int main(){
  	int x,st,ans=0;
  	while(scanf("%d",&x)!=EOF)h[++n]=x;
  	for(i=1;i<=n;i++){
            f[i]=1;
            for(j=1;j<i;j++){
                if(h[i]>h[j]&&f[i]<f[j]+1){
                   f[i]=f[j]+1;
                   p[i]=j;
            // 若是第j个高度大于第i个则可将j加入该上升子序列,变为以j结束的子序列
  		}
  	     }
  	     if(ans<f[i]){
  	        ans=f[i];
  	        st=i;
  	     }
  	}
  	cout<<"max="<<ans<<endl;
  	out(st);
  	return 0;
  }

二.友好城市

题目描述:

Palmis国有一条横贯东西的大河,何有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。
每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。
编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航线不想交的情况下,被批准的申请尽量多。

输入格式:

第1行,一个整数N(1 <= N <= 5000),表示城市数。 第2行到第n+1行,每行两个整数,中间用1个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。(0 <= xi <= 10000)

输出格式:

仅一行,输出一个整数,表示政府所能批准的最多申请数。
开始做的时候,感觉可以一个一个航线加进来,若是该航线与已加进来的航线交叉,则不加入此航线。
但这样做太麻烦了并且结果往往不是正解,所以我开始转变思路:
若是按照南岸坐标从小到大排列,两条航线若不相交,则可看作是一个上升序列,因此题目就变成了求最长上升子序列。这样问题就迎刃而解了

完整代码:

  #include<bits/stdc++.h>
  using namespace std;//01bb
  #define N 5050
  int f[N],j;
  struct stu{
  	int x,y;
  	bool operator <(const stu &stu1)const{
  		return x<stu1.x;
  	}
  }s[N];
  int n,i,ma;
  int main(){
  	cin>>n;
  	for(i=1;i<=n;i++){
  	    scanf("%d%d",&s[i].x,&s[i].y);
  	    ma=max(ma,s[i].y);
  	}
  	sort(s+1,s+1+n);
  	for(i=1;i<=n;i++){
  	    f[i]=1;
  	    for(j=1;j<i;j++){
                if(s[i].y>s[j].y&&f[i]<f[j]+1){
                   f[i]=f[j]+1;
                }
  	    }
  	}
  	int ma=0;
  	for(i=1;i<=n;i++){
  	    ma=max(ma,f[i]);
  	}
  	cout<<ma;
  	return 0;
  }
 //f[j]表示以j结尾的最长上升子序列的长度,最长的上升子序列不一定以n结尾,所以要遍历求出最大值。

三.打鼹鼠

题目描述:

鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的。根据这个特点阿Q编写了一个打鼹鼠的游戏:在一个nn的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果i时刻鼹鼠在某个网格中出现,而机器人也处于同一网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格,即从坐标为(i,j)的网格移向(i-1, j),(i+1, j),(i,j-1),(i,j+1)四个网格,机器人不能走出整个nn的网格。游戏开始时,你可以自由选定机器人的初始位置。现在你知道在一段时间内,鼹鼠出现的时间和地点,希望你编写一个程序使机器人在这一段时间内打死尽可能多的鼹鼠。

输入格式:

第一行为n(n<=1000), m(m<=10000),其中m表示在这一段时间内出现的鼹鼠的个数,接下来的m行每行有三个数据time,x,y表示有一只鼹鼠在游戏开始后time个时刻,在第x行第y个网格里出现了一只鼹鼠。Time按递增的顺序给出。注意同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出现一只鼹鼠。

输出格式:
仅包含一个正整数,表示被打死鼹鼠的最大数目

题目看着很长很吓人,其实就是在一个平面内,鼹鼠们会在不同时间、不同位置出现,同一时刻会再不同地点出现鼹鼠,一个地点在某一时刻只会出现一只鼹鼠。机器人从任意地点开始走,一个单位时间走一个单位长度,不可以斜着走。求最多能打死的鼹鼠个数。这道题本质上也是求最大上升子序列,只不过是三维的,不是很好想。

完整代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 1005
#define l 10005
struct stu{
    int x,y,t;
}s[l];
int f[N],n,m,i,j,ans;
int dis(int i,int j){
    return abs(s[i].x-s[j].x)+abs(s[i].y-s[j].y);
}
int main(){
    cin>>n>>m;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&s[i].t,&s[i].x,&s[i].y);
        f[i]=1;
        for(j=1;j<i;j++){ 
            if(s[i].t-s[j].t>=dis(i,j)){
               f[i]=max(f[i],f[j]+1);
            }
            ans=max(ans,f[i]);
        }
     }
    cout<<ans;
    return 0;
}
posted @ 2024-02-17 16:35  无敌の暗黑魔王  阅读(13)  评论(0)    收藏  举报