动态规划 ,又叫 DP(Dynamic Programming),是在0世纪50年代初,美国数学家 贝尔曼(R.Bellman) 等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。运用在暴力枚举、dfs、bfs、区间最短路等
动态机: 动态机是一种专门做动态规划(DP)的方法,是把DP的每一步都枚举出来两种状态,比如拿小球问题,你要想出拿小球会怎么样,不拿小球会怎么样,从两种方法中求最优解,从而推出公式
这次的题在我的
l
u
o
g
u
luogu
luogu 团队,点击此处跳转
大盗阿福
题目描述
阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。
这条街上一共有 N 家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。
作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?
输入格式
输入的第一行是一个整数 T (T <= 50) ,表示一共有 T 组数据。
接下来的每组数据,第一行是一个整数 N (1 <= N <= 100, 000) ,表示一共有 N 家店铺。第二行是 N 个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的现金数量均不超过 1000 。
输出格式
对于每组数据,输出一行。该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。
样例 #1
样例输入 #1
2
3
1 8 2
4
10 7 6 14
样例输出 #1
8
24
提示
对于第一组样例,阿福选择第 2 家店铺行窃,获得的现金数量为 8 。
对于第二组样例,阿福选择第 1 和 4 家店铺行窃,获得的现金数量为 10 + 14 = 24 。
这道题说是不能盗窃两个相邻的店铺,所以代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int a[N],dp[N][105];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]); //读入scanf更快
}
for(int i=1;i<=n;i++)
{
dp[i][0]=max(dp[i-1][1],dp[i-1][0]); //dp
dp[i][1]=dp[i-1][0]+a[i];
}
printf("%d\n",max(dp[n][1],dp[n][0]));
}
return 0;
}
[USACO08FEB] Dining Cows B
题目描述
为了避免餐厅过分拥挤,FJ要求奶牛们分2批就餐。每天晚饭前,奶牛们都会在餐厅前排队入内,按FJ的设想,所有第2批就餐的奶牛排在队尾,队伍的前半部分则由设定为第1批就餐的奶牛占据。由于奶牛们不理解FJ的安排,晚饭前的排队成了一个大麻烦。 第i头奶牛有一张标明她用餐批次D_i(1 <= D_i <= 2)的卡片。虽然所有N头奶牛排成了很整齐的队伍,但谁都看得出来,卡片上的号码是完全杂乱无章的。 在若干次混乱的重新排队后,FJ找到了一种简单些的方法:奶牛们不动,他沿着队伍从头到尾走一遍,把那些他认为排错队的奶牛卡片上的编号改掉,最终得到一个他想要的每个组中的奶牛都站在一起的队列,例如112222或111122。有的时候,FJ会把整个队列弄得只有1组奶牛(比方说,1111或222)。 你也晓得,FJ是个很懒的人。他想知道,如果他想达到目的,那么他最少得改多少头奶牛卡片上的编号。所有奶牛在FJ改卡片编号的时候,都不会挪位置。
输入格式
第1行: 1个整数:N * 第2…N+1行: 第i+1行是1个整数,为第i头奶牛的用餐批次D_i
输出格式
一行: 输出1个整数,为FJ最少要改几头奶牛卡片上的编号,才能让编号变成他设想中的样子。
样例 #1
样例输入 #1
7
2
1
1
1
2
2
1
样例输出 #1
2
样例 #2
样例输入 #2
5
2
2
1
2
2
样例输出 #2
1
提示
1 <= N <= 30000
这道题要的是 全黑 ,或者 全白 ,或者 前面是白,后面是黑 (2是黑,1,是白)
所以代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=30000+10;
int a[N],dp[N][105];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]; //cin也可以
}
for(int i=1;i<=n;i++)
{
dp[i][1]=dp[i-1][1]+(a[i]==2); //dp
dp[i][2]=min(dp[i-1][1],dp[i-1][2])+(a[i]==1);
}
cout<<min(dp[n][1],dp[n][2])<<endl;
return 0;
}
最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n n n,表示序列长度。
第二行有 n n n 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出
1
1
1、
2
2
2、
3
3
3、
4
4
4 即可。
这道题很简单,所以直接亮代码:
#include <bits/stdc++.h>
using namespace std;
const int N=30000+10;
int a[N],dp[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[i]>a[j]) //if判断a[i]是否大于a[j],因为for循环的时候已经是j<i就不用在if里判断
{
dp[i]=max(dp[i],dp[j]+1);
}
}
}
int res=0;
for(int i=1;i<=n;i++)
{
res=max(res,dp[i]);
}
cout<<res<<endl;
return 0;
}