蛋糕(區間DP)+東方記者(普通DP)

最近考試總是DP簽到題,本身就是個弱項, 寫個小結

蛋糕

大意

Solution

爲了簡化問題(名稱)
先聲明“我”是第一個拿蛋糕的那個人
“我”想要得到最大值
定義\(dp[i][j]\)表示取完區間\(i,j\)我能得到的最大值並且最後一次是我取走的
考慮如何轉移
對答案有貢獻的區間長度爲奇數
因爲根據定義要保證最後一次是我取的

  • 最後一次我取走了\(a[i]\)
    那麼\(dp[i][j]\)將從狀態\(dp[i+1][j]\)轉移得到
    又因爲區間長度爲奇數,這一次轉移的最後一次是另一個人取的
    考慮再往前一步的操作
    就是另一個人取走了\(a[i+1]\)或者\(a[j]\)

    • 假設另一人取走了\(a[i+1]\)
      那麼上一步的狀態變爲\(dp[i+2][j]\)
      因爲在這一步當中狀態方程表示的是\(dp[i+2][j]\)證明整段區間已經被選完了
      所以另一個人是在\(a[i+1]\)\(a[j+1]\)中選擇了\(a[i+1]\)
      當且僅當在\(a[i+1]>a[j+1]\)時成立
    • 假設另一人取走了\(a[j]\)
      同理推斷另一人是在\(a[j]\)\(a[i]\)當中選擇了前者
      上一步狀態變爲\(dp[i+1][j-1]\)
      當且僅當在\(a[j] > a[i]\)時成立
  • 最後一次我取走了\(a[j]\)
    狀態將從\(dp[i][j - 1]\)轉移得到
    另一個人取走了\(a[i]\)\(a[j-1]\)

    • 假設另一人取走了\(a[i]\)
      區間\(i+1, j\)已經被選完了
      狀態變爲\(dp[i+1][j-1]\)
      \(a[i]>a[j]\)時成立
    • 假設另一人取走了\(a[j-1]\)
      區間\(i, j-2\)已經被選完了
      狀態變爲\(dp[i][j-2]\)
      \(a[j-1]>a[i-1]\)時成立

判斷邊界可以出代碼了

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
#define max(a, b) ({register int AA = a, BB = b; AA > BB ? AA : BB;})
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 4004;

int dp[ss][ss], a[ss];

signed main(){
	register int ans = 0;
	register int n = read();
	for(register int i = 1; i <= n; i++)
		a[n + i] = a[i] = read();
	for(register int i = 1; i <= n + n; i++)
		dp[i][i] = a[i];
	for(register int len = 3; len <= n; len += 2){
		for(register int i = 1; i + len - 1 <= n + n; i++){
			register int j = i + len - 1;
			if(a[i + 1] > a[j + 1] && j + 1 <= n + n && i + 1 <= n + n) dp[i][j] = max(dp[i][j], dp[i + 2][j] + a[i]);
			if(a[i] < a[j]) dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + a[i]);
			if(a[i] > a[j]) dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + a[j]);
			if(a[i - 1] < a[j - 1] && i - 1 >= 1 && j - 1 >= 1) dp[i][j] = max(dp[i][j], dp[i][j - 2] + a[j]);
			ans = max(ans, dp[i][j]);
		}
	}
	cout << ans << endl;
	return 0;
}

東方記者

大意

在座標系中給出n個點(信息),以及移動距離的限制
距離表示爲曼哈頓距離
問在移動限制內最多能走到多少點(採集多少信息)

Solution

定義\(dp[i][j]\)表示走到第\(i\)個點收集\(j\)個信息的最小的代價
考慮轉移
\(dp[j][k]\)\(dp[i][k+1]\)證明從\(j\)走到了\(i\),收集到了第\(k+1\)個信息
代價爲從\(j\)\(i\)的曼哈頓距離
統計答案
枚舉對於每個點能收集\(j\)個信息
如果走到當前點收集\(j\)個信息的代價比限制要少
\(j\)更新答案即可

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define max(a, b) ({register long long AA = a, BB = b; AA > BB ? AA : BB;})
#define min(a, b) ({register long long AA = a, BB = b; AA < BB ? AA : BB;})
#define getdis(i, j) (abs(x[i] - x[j]) + abs(y[i] - y[j]))
using namespace std;

inline long long read(){
	register long long x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 105;

long long dp[105][105];
long long x[ss], y[ss];

signed main(){
	register int n = read();
	memset(dp, 0x7f, sizeof dp);
	for(register int i = 1; i <= n; i++){
		x[i] = read(), y[i] = read();
		dp[i][1] = abs(x[i]) + abs(y[i]);
	}
	register long long d = read();
	for(register int i = 2; i <= n; i++)
		for(register int j = 1; j <= i - 1; j++)
			for(register int k = 1; k <= j; k++)
				dp[i][k + 1] = min(dp[i][k + 1], dp[j][k] + getdis(i, j));
	register long long ans = 0;
	for(register int i = 1; i <= n; i++)
		for(register int j = 1; j <= i; j++){
			if(abs(x[i]) + abs(y[i]) + dp[i][j] <= d)//在j處收集的代價足夠,收集j
				ans = max(ans, j);
		}
	printf("%lld\n", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章