题目链接: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
**/