一道區間dp題

題意

你有一個有序的數組,現在要插入一個新的數,相信你一定學過二分查找,也知道最壞情況下需要比較幾次才能找到新的數該插入什麼位置,但是現在,我們稍微改變下套路,把新的數與數組中的每一個數比較都會有一個特定的代價,代價在1-9之間,求最壞情況下,假設你採用最優的比較策略,你會花費多少費用插入新的數。

數據範圍

t(t10)t(t\le 10)組數據,數組大小n100000n\le 100000

解法

首先考慮一個常規的區間dp:f[l][r][l,r]f[l][r]表示查詢[l,r]中任意一個數的最大代價
轉移:f[l][r]=mink=lr(max(f[l][k1],f[k+1][r])+a[k])f[l][r]=min_{k=l}^r(max(f[l][k-1],f[k+1][r])+a[k])
這樣做是O(n3)O(n^3)的,一般來見似乎沒有什麼辦法優化,但是我們發現最終答案非常的小,所以考慮換一種dp狀態:
f[l][k]lkf[l][k]表示以l爲左節點,代價爲k時最遠的右端點
轉移就也是把兩個區間拼起來

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int t,n,a[maxn],at[maxn][10],las[maxn],f[maxn][200];
char s[maxn];
signed main(){
	t=read();
	for(int x=1;x<=t;x++){
		scanf("%s",s+1);n=strlen(s+1);
		memset(f,0,sizeof(f));memset(las,0,sizeof(las));
		for(int i=1;i<=n;i++){
			a[i]=s[i]-'0';
		}
		for(int i=1;i<=n;i++){
			las[a[i]]=i;
			for(int j=1;j<=9;j++)at[i][j]=las[j];//at是序列自動機
		}
		for(int i=1;i<=n+1;i++){
			for(int j=0;j<=180;j++){
				f[i][j]=i-1;
			}
		}
		for(int i=n;i>=1;i--){//倒着轉移
			for(int j=1;j<=180;j++){
				f[i][j]=f[i][j-1];
				for(int d=1;d<=min(j,9);d++){
					int p=at[f[i][j-d]+1][d];
					if(p<i)continue;
					f[i][j]=max(f[i][j],f[p+1][j-d]);
				}
			}
		}
		for(int i=0;i<=180;i++)
		if(f[1][i]==n){printf("Case #%d: %d\n",x,i);break;}
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章