前言


某位大佬曾经说过,dp不会没问题,想不到状态转移方程没问题,多做题就会了。所以,我打算多刷点dp题。那么,先从基础刷起吧。

1. P1091 合唱队形


题意

已知序列 $a$ 有 $n$ 个数,通过取出其中一些数可以使他满足严格的先增再减序列,问最少取出几个。


思路

很显然想要求最少取出几个,我们就看严格先增再减的序列的最长长度即可。我们可以用 $g[i]$ 来存储到 $a[i]$ 为止的最长递增子序列的长度,然后用 $l[i]$ 来存储从 $a[i]$ 到序列末尾最长的递减子序列的长度。处理 $g[i]$ 从前往后扫,处理 $l[i]$ 需要从后往前扫。处理完 $f$ 和 $g$ 数组那么就从左到右扫一遍,$ans=max(ans,g[i]+l[i]-1)$ 。答案即是 $n-ans$ 。


代码实现

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>

using namespace std;

const int maxn=105;
int n;
int a[maxn],g[maxn],l[maxn];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<i;j++)
		{
			if(a[i]>a[j]) g[i]=max(g[i],g[j]+1);
		}
	}
	for(int i=n;i>=1;i--)
	{
		for(int j=n+1;j>i;j--)
		{
			if(a[i]>a[j]) l[i]=max(l[i],l[j]+1);
		}
	}
	int maxout=0;
	for(int i=1;i<=n;i++)
	{
		maxout=max(maxout,g[i]+l[i]-1);
	}
	printf("%d",n-maxout);
	return 0;
} 

2. P1280 尼克的任务


题意

尼克的一个工作日为 $n$ 分钟,从第一分钟开始到第 $n$ 分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第 $p$ 分钟开始,持续时间为 $t$ 分钟,则该任务将在第 $p+t-1$ 分钟结束。(实在不会总结题意,就直接复制过来了)


思路

我们可以设 $f[i]$ 为时间从 $i\sim n$ 所能获得最长空闲时间,最终 $f[1]$ 对应的就是答案。假设在这个 $i$ 分钟有 $k[i]$ 个任务可以,那么我们可以分以下情况转移

  • $k[i]=0$ , 那么 $f[i]=f[i+1]+1$

  • $k[i]\not=0$ ,那么可以循环 $1 \sim k[i]$ 遍历这个时间点开始的任务,$f[i]=max(f[i],f[i+k[i].t])$

    思路是这样的,但是我们记录 $k[i].t$ 并不好记录,因此我们可以先将任务开始时间按降序排序,用一个一个变量 $cnt$ 来代表已经取到第几个任务了,那么这样一直取下去,最终就能够遍历所有的任务。


代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>

using namespace std;

const int maxn=10005;
struct task
{
	int l,r;
}t[maxn];
int cmp(struct task a,struct task b)
{
	return a.l>b.l;
}
int dp[maxn];
int p[maxn];
int cnt=1,n,k; 
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d",&t[i].l,&t[i].r);
		p[t[i].l]++;
	}
	sort(t+1,t+1+k,cmp);
	for(int i=n;i>=1;i--)
	{
		if(!p[i])
		{
			dp[i]=dp[i+1]+1;
		}
		else 
		{
			for(int j=1;j<=p[i];j++)
			{
				dp[i]=max(dp[i],dp[i+t[cnt].r]);
				cnt++;
			}
		}
	}
	printf("%d\n",dp[1]);
	return 0;
}

我太懒了····就写了两道,明天继续加油吧。。