題目鏈接:https://codeforces.ml/contest/1312/problem/E
題目大意:
給出一段序列,有一種操作可以將任意兩個相同的數字合併爲一個,最序列最後最少剩多少個數字?
題目思路:
下面介紹兩種解法,O(n^2)與O(n^3)
part_1 :
考慮用dp[i][k]表示最後的答案,那麼即有:
這個轉換是通用的,但是怎麼表示合併呢?
考慮到如果區間[i,k]可以合併的話,那最後必然是一個數字,無論有幾種合併方法,最後一個數字必然會相同,所以增加一個w[i][k]數組表示,w[i][k]合併的那個數,合併不可以即爲0。
那麼就可以增加判斷條件:
考慮到合併的數字最終狀態肯定相同所以w[i][k]不需要再加什麼特判了
Code_1:
/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=2e18;
const int maxn=1e6+6;
const int mod=1000000007 ;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll num[maxn];
ll dp[505][505],w[505][505];
int main()
{
read(n);
for(int i=1;i<=n;i++)read(num[i]);
for(int i=1;i<=n;i++){
for(int k=1;k<=n;k++){
if(i==k) dp[i][k] = 1,w[i][k] = num[i];
else dp[i][k] = k-i+1;
}
}
for(int len=1;len<=n;len++){
for(int i=1;i+len<=n;i++){
int e = i+len;
for(int k=i;k<=e-1;k++){
dp[i][e] = min(dp[i][e],dp[i][k]+dp[k+1][e]);
if(dp[i][k]==1&&dp[k+1][e]==1&&w[i][k]==w[k+1][e]){
dp[i][e] = 1;
w[i][e] = w[i][k]+1;
}
}
}
}
printf("%lld\n",dp[1][n]);
return 0;
}
/**
01
**/
Part_2:
考慮dp[i]直接表示狀態,就會延伸出一個很常見的dp套路
s表示 [i,k]最終可以合併爲多少個
至於s的取值,直接用棧的思想去跑括號匹配即可
O(n^2)也就誕生:
Code_2
/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=2e18;
const int maxn=1e6+6;
const int mod=1000000007 ;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll num[maxn];
ll dp[505];
int st[maxn];
int main()
{
read(n);
for(int i=1;i<=n;i++){
read(num[i]);
dp[i] = 1e8+7;
}
for(int i=1;i<=n;i++){
int s = 0;
for(int k=i;k>=1;k--){
if(!s) st[++s] = num[k];
else{
ll now = num[k];
while(s&&st[s]==now){
s--;
now++;
}
st[++s] = now;
}
dp[i] = min(dp[i],dp[k-1]+s);
}
}
printf("%lld\n",dp[n]);
return 0;
}
/**
01
**/