BTTCOJ 问题 E: 维和部队 线性动规

题目描述

在2019国庆大阅兵仪式上,蓝色贝雷帽,荒漠迷彩服,维和部队方队阔步走来。中国是联合国安理会常任理事国中派出维和人员最多的国家。在联合国7个维和任务区,2500多名中国军人守护在最危险的地方,还有8000名维和待命官兵随时听令出征。中国无战事,军人有牺牲。先后有13名维和勇士牺牲在异国他乡,中国军人用生命和热血彰显维护世界和平的大国担当!向维和部队官兵致敬!向中国军人致敬!
现在,维和部队每天都收到很多的维和任务命令,假设维和部队所在的维和区域可以抽象为一个N*N大小的矩阵形区域,并且认为矩阵形区域的左上角坐标为<1,1>,右下角坐标为<N,N>。现在联合国军事指挥所开始陆续地在我维和区域下达维和任务的命令,命令格式如下(详见数据样例):
TIME ROW COL
该命令格式解读为:在TIME时刻,在矩阵形区域的<ROW,COL>坐标处有一个维和任务,如果要完成该任务,部队必须在TIME时刻到达这里,也就是说,如果维和部队到达<ROW,COL>的时刻不是TIME,则认为这个任务失败。现在维和部队正乘坐武装直升机在维和区域上空待命,他们随时准备空降到矩形区域的任何一个点上,假设空降到地面的时刻为1时刻,落地之后每过一个时间单位,他们只能向直接相邻的上、下、左、右四个方向移动一个距离单位,或者保持在原地不动。维和区域情况非常复杂,每一个维和任务确实不一定能够完成,现在你作为我维和部队的参谋长,请你根据维和任务数据和规则,计算在给定的任务中做多能够完成几个?

输入格式

第一行输入两个正整数N, M,空格分隔,分别代表矩形区域的大小和维和任务数量,1<=N<=1000,1<=M<=10000
接下来M行每行三个数,空格分隔,代表在时刻TIME,矩形区域的横坐标ROW,纵坐标COL处有一个任务。
数据保证任何两个任务都是不同的,同一时刻最多只有一个任务命令。

输出格式

输出维和部队最多能够完成的任务数量。输出数据完成后输出回车换行。

输入样例

7 7
3 6 3
4 4 7
5 7 3
1 5 3
2 3 5
7 1 3
6 2 3

输出样例

3

数据范围与提示

部队在1时刻空降到<5,3>完成一个任务,然后等待一个时刻,在时刻3到达<5,4>去完成另一个任务,然后原地等待一个时刻,在时刻5到达<7,3>在完成一个任务。这样部队最多可以完成3个任务,没有比这样的办法能完成的任务更多的其他办法了。在这里插入图片描述

解题思路

当时比赛的时候看这个题是真的懵了,以为是个优先队列加广搜,时间不够就没做(幸好当时没做- -),下来被大佬告知是一道动规题,初学动规的小萌新便去刷了一下动规视频和题,回过头来看这个题,这不就是最长上升子序列?????WDF,温习一下解题步骤:
1.将问题分解成子问题,正解是分解为以k为终点长度最长的上升子序列,不能是求前k个元素的最长上升子序列的长度,假设f(n)=x,有可能有多个序列满足f(n)=x,有的序列的最后一个元素比an+1小,则加上an+1就能形成更长的子序列;有的序列的最后一个元素比an+1大,那么以后的事情受如何达到状态n的影响,不具备无后效性。
2.确定状态,子问题只和一个变量-数字的位置有关,因此序列中数的位置k就是状态,而k对应的值就是就是以ak为终点的最长上升子序列,状态一共有M个。
3.找出状态转移方程

for(int i=2;i<=M;++i)//每次求以第i个数为终点的最长上升子序列的长度
{
	for(int j=1;j<i;++j)//查看以第j个数为终点的最长上升子序列
	{
		int time=abs(cd[i].x-cd[j].x)+abs(cd[i].y-cd[j].y);//将位置距离换算成时间
                if(time<=abs(cd[i].t-cd[j].t))
                {
                	max_len[i]=max(max_len[i],max_len[j]+1);//状态转移方程
                }
	}
}

无后效性:当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=10005;
struct cp
{int x,y,t;}
cd[maxn];
int max_len[maxn];//存以k为终点的最长上升子序列
bool cmp(cp a,cp b)
{
    return a.t<b.t;
}
int main()
{
    int N,M;
    while(cin>>N>>M)
    {
        for(int i=1;i<=M;i++)//这边不用下标0,因为下标代表第几个需要营救的位置,方便理解
        {
            cin>>cd[i].t>>cd[i].x>>cd[i].y;
            max_len[i]=1;
        }
        sort(cd+1,cd+M+1,cmp);
        for(int i=2;i<=M;i++)
        {
            for(int j=1;j<i;j++)
            {
                int time=abs(cd[i].x-cd[j].x)+abs(cd[i].y-cd[j].y);
                if(time<=abs(cd[i].t-cd[j].t))
                {
                    max_len[i]=max(max_len[i],max_len[j]+1);//j最大为i-1,那么以i为终点的最长上升子序列就是lax_len[j]+1,因为他本身也满足
                }
            }
        }
        cout<<*max_element(max_len+1,max_len+M+1)<<endl;
    }
    return 0;
}
发布了6 篇原创文章 · 获赞 18 · 访问量 198

猜你喜欢

转载自blog.csdn.net/qq_44758960/article/details/104979912
今日推荐