1. 程式人生 > >Gym - 101670F Shooting Gallery(CTU Open Contest 2017 區間dp)

Gym - 101670F Shooting Gallery(CTU Open Contest 2017 區間dp)

能夠 tac 自己 attach ref printf opened efi can

題目&題意:(有點難讀...)

給出一個數字序列,找出一個區間,當刪除這個區間中的兩個相同的數字後,只保留這兩個數字之間的序列,然後繼續刪除相同的數字,問最多可以實行多少次刪除操作。

例如:

技術分享圖片

所以執行兩次刪除操作。

思路:

區間dp,關鍵在於確定大的區間是由哪些小的區間轉化來的。

當a[l] == a[r]的時候,dp[l][r] = dp[l+1][r-1]+1(因為要得到最多的刪除次數,大的區間的次數在相等的情況下肯定是由內部小的區間加一得來的);

當a[l] != a[r]的時候,dp[l][r] = max(dp[l+1][r],dp[l][r-1])(這個自己模擬的出的......)

代碼:

內層循環枚舉長度:

技術分享圖片
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define FRE() freopen("in.txt","r",stdin)
using namespace std;
typedef long long ll;
const int maxn = 5000+10;
int dp[maxn][maxn];
int a[maxn];

int main(){
    //FRE();
    int n;
    while(scanf("%d",&n)!=EOF){
        for(int i=1; i<=n; i++){
            scanf(
"%d",&a[i]); } for(int i = 1; i<=n; i++){ for(int j = 1; j<=n; j++){ dp[i][j] = 0; } } //memset(dp,0,sizeof(dp)); for(int k=1; k<n; k++){//枚舉區間長度 for(int l=1; l+k<=n; l++){//枚舉區間的起點 if
(a[l] == a[l+k]){ dp[l][l+k] =dp[l+1][l+k-1]+1; } else{ dp[l][l+k] = max(dp[l+1][l+k],dp[l][l+k-1]); } } } printf("%d\n",dp[1][n]); } return 0; } /* PutIn: 3 6 6 6 12 3 14 15 92 65 35 89 79 32 38 46 26 12 3 1 4 1 5 9 2 6 5 3 5 9 7 2 7 1 8 2 8 1 4 1 6 1 8 11 1 2 4 8 16 32 16 8 4 2 1 6 1 2 3 1 2 3 PutOut: 1 0 2 2 1 5 1 */
View Code

內層循環枚舉區間右端點

技術分享圖片
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define FRE() freopen("in.txt","r",stdin)
using namespace std;
typedef long long ll;
const int maxn = 5e3+10;
int dp[maxn][maxn];
int a[maxn];

int main(){
    //FRE();
    int n;
    while(scanf("%d",&n)!=EOF){
        for(int i=1; i<=n; i++){
            scanf("%d",&a[i]);
        }
        for(int i = 1; i<=n; i++){
            for(int j =1; j<=n; j++){
                dp[i][j] = 0;
            }
        }
        for(int l=n; l>=0; l--){//只能逆序來枚舉起點,正序大區間的值無法更新
            for(int r=l+1; r<=n; r++){
                if(a[l] == a[r]){
                    dp[l][r] = dp[l+1][r-1]+1;
                }else {
                    dp[l][r] = max(dp[l+1][r],dp[l][r-1]);
                    //dp[l][r] = max(dp[l][r-1],dp[l][r]);
                }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}
View Code

區間dp過程大致相同:

第一層循環枚舉區間的長度,第二層循環枚舉區間的起點。

第二層又有兩種情況:

第一種:需要在[st,en]中找一個分割點k使得將[st,en]分成[st,k]和[k+1,en]這樣兩個區間能夠得到最優解。

第二種:[i,j]可以由[i,j-1]或者[i,j+1]轉移過來。這種轉移關系肯定是有具體的情況推出的,不是一成不變的。

Gym - 101670F Shooting Gallery(CTU Open Contest 2017 區間dp)