kuangbin带你飞 专题十二 基础DP
- HDU 1024 Max Sum Plus Plus
- HDU 1029 Ignatius and the Princess IV
- HDU 1069 Monkey and Banana
- HDU 1074 Doing Homework
- HDU 1087 Super Jumping! Jumping! Jumping!
- HDU 1114 Piggy-Bank
- HDU 1176 免费馅饼
- HDU 1260 Tickets
- HDU 1257 最少拦截系统
- HDU 1160 FatMouse's Speed
- POJ 1015 Jury Compromise
- POJ 1458 Common Subsequence
- POJ 1661 Help Jimmy
- POJ 2533 Longest Ordered Subsequence
- POJ 3186 Treats for the Cows
- HDU 1078 FatMouse and Cheese
- HDU 2859 Phalanx
- POJ 3616 Milking Time
- POJ 3666 Making the Grade
HDU 1024 Max Sum Plus Plus
大意:
从一序列中取出若干段,这些段之间不能交叉,使得和最大并输出。
思路:
动态规划 首先我们可以列出最基本的状态转移方程:
\(dp[i][j] = max( dp[i][j-1] + a[j] , dp[i-1][k] + a[j ]) i-1<=k<=j-1\)
这个方程的含义是:
$dp[i][j] $是将前 j 个数分成 i 份,且第 i 份包含第 j 个数 的情况下的最大值
那么对于第 j 个数来说,就有两个选择:
作为第 i 份的一部分 :也就是将前 j-1 个数分成 i 份 且第 j-1 个数属于第 i 份 即 \(dp[i][j-1]\)
或者单独出来成为第 i 份:也就是将前 j-1 个数分成 i-1 份 且第 j-1 个数不一定属于第 i-1 份 即\(dp[i-1][k] i-1<=k<=j-1\)
但是这个方程不仅时间复杂度高,空间复杂度也高的可怕 这是不行的
所以我们要将其优化:
首先我们发现$ dp[i][j] \(只需要比较\) dp[i][j-1] \(与\) dp[i-1][k] \(的最大值即可 而这个\) dp[i-1][k] $的最大值是可以记录下来的 不需要遍历 这就砍去了一层循环
所以我们只需要定义一个 pre[n] 数组 用 pre[j] 来存储第 j-1 个数被分成 i-1 份时的最大值即可
于此同时 在计算$ dp[i][j] \(时,我们可以计算出\) dp[i][k] i<=k<=j \(的值 而这个值是在之后我们要计算\) dp[i+1][j+1] $时 要使用的 pre[j]
现在状态转移方程变成了:
$ dp[i][j] = max( dp[i][j-1] + a[j] , pre[j-1] + a[j ]) $
现在我们发现 由于pre[j] 的存在 似乎已经不需要 $dp[i][j] \(这个庞大的二维数组了 只需要开一个 dp[n] 的数组 用dp[j]来存储\)dp[i][j]$即可,因为当前的转移方程根本就没有用到 i 这一维!
这样的话 转移方程又变成了:
$dp[j] = max( dp[j-1] + a[j] , pre[j-1] + a[j ]) $
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int a[N], n, m;
LL pre[N], dp[N];
int main() {
while (scanf("%d", &m) != EOF) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pre[i] = 0;
dp[i] = 0;
}
pre[0] = dp[0] = 0;
LL anss,temp;
for (int i = 1; i <= m; i++) {
temp = -0x3f3f3f3f;
for (int j = i; j <= n; j++) {
dp[j] = max(dp[j - 1] + a[j], pre[j - 1] + a[j]);
pre[j - 1] = temp;
temp = max(temp, dp[j]);
}
}
cout << temp << endl;
}
return 0;
}
HDU 1029 Ignatius and the Princess IV
大意:
求数列中出现次数大于n/2的数字
思路:
不需要dp...不知道为什么分到了这个专题里,直接求即可,优化了空间复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n;
int main(){
while(cin>>n){
int x, cnt, res;
cin >> x;
cnt = 1;
res = x;
n--;
while(n--){
cin>>x;
if (x == res) cnt++;
else cnt--;
if(cnt<0){
cnt = 1;
res = x;
}
}
cout << res << endl;
}
return 0;
}
HDU 1069 Monkey and Banana
大意:
给出n个种类的正方体,每种都有无穷多数量,现在要求搭建一个塔,从下到上用到的正方体是严格满足上面的边小于下面的边的,问最高能搭多高
思路:
首先需要将n个种类的正方体的六种摆放方式都存下来,然后\(dp[i]\)代表以第i个正方体为顶的塔的高度,那么\(n^2\)去枚举,符合严格小于的条件就更新即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n;
struct node{
int x, y, z;
} a[N];
bool cmp(node a,node b){
if (a.x == b.x) return a.y > b.y;
else return a.x > b.x;
}
int dp[N],t=0;
int main(){
while(scanf("%d",&n)&&n!=0){
t++;
for (int i = 0; i < n;i++){
cin >> a[i * 6].x >> a[i * 6].y >> a[i * 6].z;
a[i * 6 + 1].x = a[i * 6].y, a[i * 6 + 1].y = a[i * 6].x, a[i * 6 + 1].z = a[i * 6].z;
a[i * 6 + 2].x = a[i * 6].y, a[i * 6 + 2].y = a[i * 6].z, a[i * 6 + 2].z = a[i * 6].x;
a[i * 6 + 3].x = a[i * 6].x, a[i * 6 + 3].y = a[i * 6].z, a[i * 6 + 3].z = a[i * 6].y;
a[i * 6 + 4].x = a[i * 6].z, a[i * 6 + 4].y = a[i * 6].x, a[i * 6 + 4].z = a[i * 6].y;
a[i * 6 + 5].x = a[i * 6].z, a[i * 6 + 5].y = a[i * 6].y, a[i * 6 + 5].z = a[i * 6].x;
}
sort(a, a + 6 * n, cmp);
for (int i = 0; i < 6 * n;i++){
dp[i] = a[i].z;
}
int res = 0;
for (int i = 0; i < 6 * n;i++){
for (int j = i+1; j < 6*n;j++){
if(a[j].x<a[i].x&&a[j].y<a[i].y)
dp[j] = max(dp[j], dp[i] + a[j].z);
res = max(dp[i], res);
}
}
printf("Case %d: maximum height = %d\n",t,res);
}
return 0;
}
HDU 1074 Doing Homework
大意:
给出n(\(n<=15\))个作业的用时和deadline,如果有作业超过deadline,那么每超过1天就要扣1分(超过多个作业也是1分),问扣分最少的方案,按字典序最小输出(题目保证输入按照字典序递增)
思路:
状压dp,枚举能转移过来的每个状态,更新dp数组即可
#include <bits/stdc++.h>
using namespace std;
const int N = 15 + 5;
typedef long long LL;
int T, n, d[N], t[N], dp[1 << N], l[1 << N], pre[1 << N], res[N], cnt,
step[1 << N];
string name[N];
int main() {
cin >> T;
while (T--) {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> name[i] >> d[i] >> t[i];
}
memset(dp, 0x3f, sizeof dp);
memset(l, 0, sizeof l);
dp[0] = 0;
for (int i = 0; i < (1 << n); i++) {
for (int j = 0; j < n; j++) {
if ((1 << j) & i) {
int prestate = i - (1 << j);
int temp = max(0, l[prestate] + t[j] - d[j]);
if (dp[prestate] + temp <= dp[i]) {
dp[i] = dp[prestate] + temp;
l[i] = l[prestate] + t[j];
pre[i] = prestate;
step[i] = j;
}
}
}
}
cnt = 0;
int state = ( 1 << n ) - 1;
cout << dp[state] << endl;
while (state != 0) {
res[cnt++] = step[state];
state = pre[state];
}
for (int i = cnt - 1; i >= 0; i--) {
cout << name[res[i]] << endl;
}
}
return 0;
}
HDU 1087 Super Jumping! Jumping! Jumping!
大意:
求最大权值上升子序列
思路:
把最大上升子序列的板子改改就行,dp[i]代表以i为结尾的上升子序列的权值
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, a[N], dp[N];
int main() {
while (scanf("%d", &n) && n != 0) {
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
dp[i] = a[i];
}
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i]) {
dp[i] = max(dp[j] + a[i], dp[i]);
}
}
}
int res = 0;
for (int i = 0; i < n; i++) {
res = max(res, dp[i]);
}
cout << res << endl;
}
return 0;
}
HDU 1114 Piggy-Bank
大意:
给出储钱罐的罐重和总重,以及m种硬币的数量和面值,问符合条件(即硬币重量=总重-罐重)的硬币最小的面值和为多少
思路:
完全背包,容量为总重-罐重,价值为硬币的面值
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long LL;
int T, n, v, dp[N], m, s[N], vul[N];
int main() {
cin >> T;
while (T--) {
int x, y;
cin >> x >> y;
v = y - x;
cin >> m;
for (int i = 0; i < m; i++) {
cin >> vul[i] >> s[i];
}
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 0; i < m; i++) {
for (int j = s[i]; j <= v; j ++) {
dp[j] = min(dp[j], dp[j - s[i]] + vul[i]);
}
}
if(dp[v]==0x3f3f3f3f){
cout << "This is impossible." << endl;
}
else{
cout << "The minimum amount of money in the piggy-bank is " << dp[v] << "." << endl;
}
}
return 0;
}
HDU 1176 免费馅饼
大意:
有n个馅饼在不同的时间会落到0到10的区间内,初始位置为5,每秒只能移动一米,问最多能接到多少馅饼
思路:
既可以正着写也可以反着写,正着写的话需要判断能否达到这个点,反着写就无所谓了
正着写:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef long long LL;
int n, mp[N][20], dp[N][20];
int main() {
while (scanf("%d", &n) && n != 0) {
int x, t;
int lastt = 0;
memset(mp, 0, sizeof mp);
memset(dp, 0xc0, sizeof dp);
for (int i = 0; i < n; i++) {
scanf("%d%d", &x, &t);
mp[t][x]++;
lastt = max(lastt, t);
}
dp[0][5] = 0;
for (int i = 1; i <= lastt; i++) {
for (int j = 0; j <= 10; j++) {
if (j != 0)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]);
if (j != 10)
dp[i][j] = max(dp[i][j], max(dp[i-1][j],dp[i - 1][j + 1]));
if(dp[i][j]!=int(0xc0c0c0c0)){
//cout << dp[i][j] << endl;
dp[i][j] += mp[i][j];
}
//cout << i << ' ' << j << ' ' << dp[i][j] << endl;
}
}
int res = 0;
for (int i = 0; i <= 10;i++){
res = max(res, dp[lastt][i]);
}
printf("%d\n", res);
}
return 0;
}
反着写:
#include<bits/stdc++.h>
using namespace std;
int i,j,n,dp[100005][20],maxn,x,t;
int main()
{
while(scanf("%d",&n),n!=0)
{
memset(dp,0,sizeof(dp));
maxn=0;
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&t);
dp[t][x]++;
if(t>maxn)
maxn=t;
}
for(i=maxn-1;i>=0;i--)
{
dp[i][0]+=max(dp[i+1][0],dp[i+1][1]);
dp[i][10]+=max(dp[i+1][10],dp[i+1][9]);
for(j=1;j<=9;j++)
{
dp[i][j]+=max(dp[i+1][j],max(dp[i+1][j-1],dp[i+1][j+1]));
}
}
printf("%d\n",dp[0][5]);
}
}
HDU 1260 Tickets
大意:
给出n个人买票的时间以及他们每个人和下一个人合买双人票的时间,问最早什么时候能卖完票
思路:
\(dp[i]\)代表前i个人买完票需要多久,那么可以从\(dp[i-2]\)转移过来,也可以从\(dp[i-1]\)转移过来
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 5;
typedef long long LL;
int t, n, a[N], b[N], dp[N];
int main() {
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
dp[i] = 0x3f3f3f3f;
}
for (int i = 0; i < n - 1; i++) {
cin >> b[i];
}
for (int i = 0; i < n; i++) {
if (i == 0) dp[i] = a[0];
else if (i == 1) dp[i] = min(a[0] + a[1], b[0]);
else dp[i] = min(dp[i - 2] + b[i - 1], dp[i - 1] + a[i]);
}
int res = dp[n-1];
int ss = res % 60;
int mm = (res / 60) % 60;
int hh = ((res / 60) / 60) % 60;
if (hh + 8 > 12) printf("%02d:%02d:%02d pm\n", hh - 4, mm, ss);
else printf("%02d:%02d:%02d am\n", hh + 8, mm, ss);
}
return 0;
}
HDU 1257 最少拦截系统
大意:
经典题,最长上升子序列
思路:
模板题
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, a[N], dp[N];
int main() {
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i]) dp[i] = max(dp[j] + 1, dp[i]);
res = max(res, dp[i]);
}
}
cout << res << endl;
}
return 0;
}
HDU 1160 FatMouse's Speed
大意:
给出n个老鼠的体重x和速度y,要求找出最多的一组老鼠,使他们严格符合体重上升,速度下降
思路:
先按照体重排一下序,然后求最长下降子序列即可,不过需要记录路径,开一个pre数组即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long LL;
int cnt, dp[N], res, pre[N];
struct node {
int x, y, id;
} a[N];
bool cmp(node a, node b) {
if (a.x == b.x) return a.y > b.y;
return a.x < b.x;
}
int main() {
while (scanf("%d%d", &a[cnt].x, &a[cnt].y) != EOF) {
dp[cnt] = 1;
pre[cnt] = -1;
cnt++;
a[cnt-1].id = cnt;
}
int ed = 0;
sort(a, a + cnt, cmp);
for (int i = 0; i < cnt; i++) {
for (int j = 0; j < i; j++) {
if (a[j].y > a[i].y&&a[i].x>a[j].x) {
if (dp[j] + 1 > dp[i]){
pre[i] = j;
dp[i] = dp[j] + 1;
}
}
}
if (dp[i] > res) {
res = dp[i];
ed = i;
}
}
cout << res << endl;
stack<int> s;
while (ed != -1) {
s.push(a[ed].id);
ed = pre[ed];
}
while(!s.empty()){
cout << s.top() << endl;
s.pop();
}
return 0;
}
POJ 1015 Jury Compromise
大意:
n个候选人,从中选出m个人。
控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
思路:
\(dp[i][k]\)代表选了i个人且控辩差为k的情况下,控辩和的最大值。
为了处理方便,需要将k都加上m*20,这样保证所有的k都是大于等于0的。
初始将dp数组赋值为-1,\(dp[0][m*20]=0\),只有当dp数组不等于-1时可以通过这个状态转移,同时需要保证要选的人之前没有选过,那么只需要维护一个pre数组记录达到当前状态选了哪些人即可
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 200 + 5;
typedef long long LL;
int n, m, a[N], b[N], dp[25][1000], maxk, pre[25][1000], res[25];
int cases = 0;
int main() {
while (cin >> n >> m && (n + m != 0)) {
cases++;
memset(dp, -1, sizeof dp);
memset(pre, -1, sizeof pre);
for (int i = 0; i < n; i++) {
cin >> a[i] >> b[i];
}
maxk = m * 20;
dp[0][maxk] = 0;
for (int i = 0; i < m; i++)
for (int k = 0; k <= maxk * 2; k++)
if (dp[i][k] != -1)
for (int j = 0; j < n; j++) {
if (a[j] + b[j] + dp[i][k] >
dp[i + 1][k + a[j] - b[j]]) {
int x = i, y = k;
while (x > 0 && pre[x][y] != j) {
y = y - (a[pre[x][y]] - b[pre[x][y]]);
x--;
}
if (x == 0) { // j不在路径内
pre[i + 1][k + a[j] - b[j]] = j;
dp[i + 1][k + a[j] - b[j]] =
a[j] + b[j] + dp[i][k];
}
}
}
int k = 0;
for (k = 0; k <= maxk; k++) {
if (dp[m][k + maxk] != -1) break;
if (dp[m][-k + maxk] != -1) break;
}
if (dp[m][k + maxk] > dp[m][-k + maxk])
k = k + maxk;
else
k = maxk - k;
cout << "Jury #" << cases << endl;
cout << "Best jury has value " << (k - maxk + dp[m][k]) / 2
<< " for prosecution and value " << (dp[m][k] - k + maxk) / 2
<< " for defence: " << endl;
for (int i = m; i > 0; i--) {
res[i-1] = pre[i][k]+1;
k = k - (a[pre[i][k]] - b[pre[i][k]]);
}
sort(res, res + m);
for (int i = 0; i < m; i++) cout << ' ' << res[i];
cout << endl;
}
return 0;
}
POJ 1458 Common Subsequence
lcs板子题
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
string s1, s2;
int dp[N][N];
int main() {
while (cin >> s1 >> s2) {
memset(dp, 0, sizeof dp);
s1 = " " + s1;
s2 = " " + s2;
for (int i = 1; i < s1.size(); i++) {
for (int j = 1; j < s2.size(); j++) {
if (s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
cout << dp[s1.size()-1][s2.size()-1] << endl;
}
}
POJ 1661 Help Jimmy
大意:
一个小球从\((x,y)\)位置落下,下落速度恒定为1,当落到一个平台时可以向左也可以向右走,速度也是1,走到边缘时继续下落,每次下落距离不能超过k米,现在给出n个平台的左右边缘位置和高度,问小球最快多久能落到地面
思路:
首先将平台按照高度排一下序,然后计算一下小球第一次碰到的是哪个平台,然后从这个平台开始更新dp数组。\(dp[i][0]\)代表到达第i个平台的左边缘最快时间是多少,\(dp[0][1]\)代表到达右边缘。
可以从上到下写也可以从下到上写,从上到下写需要判断dp数组是否为INF,如果为INF代表不能到达第i个平台,那么就不能利用这个平台去更新下面的平台
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
typedef long long LL;
int t, n, x, y, k, start = 0,dp[N][2],res;
struct node {
int l, r, h;
} a[N];
const int INF = 0x3f3f3f3f;
bool cmp(node a, node b) { return a.h > b.h; }
int main() {
cin >> t;
while (t--) {
cin >> n >> x >> y >> k;
for (int i = 0; i < n; i++) {
cin >> a[i].l >> a[i].r >> a[i].h;
}
memset(dp, 0x3f, sizeof dp);
sort(a, a + n, cmp);
res = INF;
a[n].l = -INF;
a[n].r = INF;
a[n].h = 0;
for (int i = 0; i <= n; i++) {
if (a[i].r < x || a[i].l > x || a[i].h > y || y - a[i].h > k)
continue;
else {
start = i;
break;
}
}
if (start == n) {
cout << y << endl;
continue;
}
dp[start][0] = abs(y-a[start].h)+abs(x - a[start].l);
dp[start][1] = abs(y-a[start].h)+abs(x - a[start].r);
for (int i = start; i < n; i++) {
if (dp[i][0] == INF) continue;
int flag1 = 0;
int flag2 = 0;
for (int j = i + 1; j < n && (flag1 + flag2 != 2); j++) {
if (a[j].r < a[i].l || a[j].l > a[i].r || a[i].h - a[j].h > k)
continue;
else {
if(flag1==0){
if(a[j].l<=a[i].l){
flag1 = 1;
dp[j][0]=min(dp[j][0],dp[i][0]+abs(a[i].l-a[j].l)+abs(a[i].h-a[j].h));
dp[j][1]=min(dp[j][1],dp[i][0]+abs(a[i].l-a[j].r)+abs(a[i].h-a[j].h));
}
}
if(flag2==0){
if(a[j].r>=a[i].r){
flag2 = 1;
dp[j][0]=min(dp[j][0],dp[i][1]+abs(a[i].r-a[j].l)+abs(a[i].h-a[j].h));
dp[j][1]=min(dp[j][1],dp[i][1]+abs(a[i].r-a[j].r)+abs(a[i].h-a[j].h));
}
}
}
}
if (flag1 == 0 && (a[i].h <= k)) res = min(res, dp[i][0] + a[i].h);
if (flag2 == 0 && (a[i].h <= k)) res = min(res, dp[i][1] + a[i].h);
}
cout << res << endl;
}
}
POJ 2533 Longest Ordered Subsequence
最长上升子序列模板题
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
int n, a[N], dp[N], res;
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i], dp[i] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
}
res = max(res, dp[i]);
}
cout << res << endl;
}
POJ 3186 Treats for the Cows
大意:
给出n个数,每次只能从剩下的数里面取第一个数或者最后一个数,价值是\(a[i]*k\),k为第几次取,问最大价值和是多少
思路
区间dp,\(dp[i][j]\)代表从剩下第i个数和到第j个数时,能取到的最大值
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 2e3 + 5;
int n, a[N], dp[N][N], res;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], dp[i][i] = n * a[i];
for (int i = n; i >= 1; i--) {
for (int j = i; j <= n; j++) {
dp[i][j] = max(dp[i + 1][j] + (n - (j - i)) * a[i],
dp[i][j - 1] + (n - (j - i)) * a[j]);
}
}
cout << dp[1][n] << endl;
}
HDU 1078 FatMouse and Cheese
大意:
给出一个矩阵,从\((0,0)\)点开始,每次只能走到元素比当前大的位置,每次可以横向或者纵向走k步,问最多能走到的位置和的最大值是多少
思路:
记忆化搜索
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 5;
typedef long long LL;
int n, k, a[N][N], dp[N][N];
int f[2][4] = {0, 0, 1, -1, 1, -1, 0, 0};
int dfs(int x, int y) {
if (dp[x][y]) return dp[x][y];
int temp = 0;
for (int i = 1; i <= k; i++) {
for (int j = 0; j < 4; j++) {
int xx = x + f[0][j] * i;
int yy = y + f[1][j] * i;
if (xx >= 0 && xx < n && yy >= 0 && yy < n && a[xx][yy] > a[x][y]) {
temp = max(temp, dfs(xx, yy));
}
}
}
return dp[x][y] = temp + a[x][y];
}
int main() {
while (cin >> n >> k && (n != -1 && k != -1)) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
dp[i][j] = 0;
}
}
cout << dfs(0, 0) << endl;
}
return 0;
}
HDU 2859 Phalanx
大意:
给出一个矩阵,要求输出最大的 延左下到右上的对角线对称的 矩阵大小
思路:
\(now[i][j]\)代表以\((i,j)\)为左上角的长度为k矩阵是否对称,那么它可以由\(pre[i-1][j]\)和\(pre[i][j-1]\)转移过来,\(pre[i][j]\)代表以\((i,j)\)为左上角的长度为k-1的矩阵是否对称,所以如果\(pre[i-1][j]\)和\(pre[i][j-1]\)均为true,那么只需要\(a[i][j]==a[i+k-1][j+k-1]\)即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
typedef long long LL;
int n, pre[N][N], now[N][N];
char a[N][N];
int main() {
while (cin >> n && n != 0) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
pre[i][j] = 1;
}
}
int res = 1;
for (int k = 2; k <= n; k++) {
int flag = 0;
for (int i = 0; i <= n - k; i++) {
for (int j = 0; j <= n - k; j++) {
if (a[i][j] == a[i + k - 1][j + k - 1] && pre[i + 1][j] &&
pre[i][j + 1])
now[i][j] = 1, flag = 1;
else
now[i][j] = 0;
}
}
if (flag)
res = k;
else
break;
for (int i = 0; i <= n - k; i++)
for (int j = 0; j <= n - k; j++) pre[i][j] = now[i][j];
}
cout << res << endl;
}
return 0;
}
POJ 3616 Milking Time
大意:
m个牛需要挤奶,他们都有一个开始时间结束时间以及价值,对于每只牛,挤完奶需要休息k分钟,最后问能挤多少价值的奶。
思路:
排序后从左到右更新即可
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
int dp[N], res;
struct node {
int l, r, v;
} a[N];
int n, m, r;
bool cmp(node a, node b) {
if (a.l == b.l) return a.r < b.r;
return a.l < b.l;
}
int main() {
cin >> n >> m >> r;
for (int i = 0; i < m; i++) {
cin >> a[i].l >> a[i].r >> a[i].v;
dp[i] = a[i].v;
}
int res = 0;
sort(a, a + m, cmp);
for (int i = 0; i < m; i++) {
dp[i] = a[i].v;
}
for (int i = 0; i < m; i++) {
for (int j = i + 1; j < m; j++) {
if (a[j].l >= a[i].r + r) dp[j] = max(dp[j], dp[i] + a[j].v);
}
res = max(res, dp[i]);
}
cout << res << endl;
}
POJ 3666 Making the Grade
大意:
给出n个数,将这个数列修改为不增序列或者不减序列,需要的最小代价是多少(每次修改的代价为修改前后的差的绝对值)
思路:
贪心的想法是每次修改必然修改到原数列中存在的数,因为不这样修改必然会修改多了。
然后先考虑修改到不减序列,那么可以先将a数组排序,得到b数组,然后\(dp[i][j]\)代表将第i个数修改到\(b[j]\)的代价最小值,那么这个最小值可以从\(min(dp[i-1][1....j])\)转移过来,但是这样复杂度很高,是\(O(n^3)\)的,怎么优化呢,注意到每次更新j时也会更新\(min(dp[i-1][1....j])\),所以直接在更新\(dp[i][j]\)的时候更新\(min(dp[i][1....j])\)即可,
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 2e3 + 5;
typedef long long LL;
int n, a[N], b[N], dp[N][N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
b[i] = a[i];
}
sort(b, b + n);
for (int i = 0; i < n; i++) dp[0][i] = min(dp[0][i], abs(a[0] - b[i]));
for (int i = 1; i < n; i++) {
int temp = dp[i - 1][0];
for (int j = 0; j < n; j++) {
temp = min(temp, dp[i - 1][j]);
dp[i][j] = temp + abs(a[i] - b[j]);
}
}
int res = dp[n - 1][0];
for (int i = 0; i < n; i++) {
res = min(res, dp[n - 1][i]);
}
cout << res << endl;
}