今天刷了一遍 kuangbin带我飞之区间dp,做一下总结
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=77874#overview
不知道看的谁的博客的一句话,区间最优解问题一般都是区间dp。有道理,资瓷呀!
NO.1 hdu 3537 cake
题意:给一个n个点的多边形(不一定是凸包),用n-3条线段切成n-2个三角形的最优划分,每次切的花费为|a.x-b.x|*|a.y-b.y|%p.
题解:首先判断是不是凸包,是的话进行最优三角划分。
dp[i][j]表示多边形点i到j的最优化分。
dp[i][j] = dp[i][k] + dp[k][j] + cal(i,k)+cal(k,j) , i<=k<=j cal(k,j)为计算连接kj线段的消耗,如果存在则为0
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 300+10;
const int inf = 1e9;
int mod,dp[N][N];
int n,t;
struct point{
int x,y;
point(){}
point(int _x,int _y):x(_x),y(_y){}
friend point operator-(const point a,const point b){
return point(a.x-b.x,a.y-b.y);
}
}p[N];
int abs(int x) {
return x < 0 ? -x : x;
}
point save[400];
int xmult(point p1,point p2,point p0){
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
bool cmp(const point& a,const point &b){
if(a.y == b.y)return a.x < b.x;
return a.y < b.y;
}
int Graham(point *p,int n) {
int i;
sort(p,p + n,cmp);
save[0] = p[0];
save[1] = p[1];
int top = 1;
for(i = 0;i < n; i++){
while(top && xmult(save[top],p[i],save[top-1]) >= 0)top--;
save[++top] = p[i];
}
int mid = top;
for(i = n - 2; i >= 0; i--){
while(top>mid&&xmult(save[top],p[i],save[top-1])>=0)top--;
save[++top]=p[i];
}
return top;
}
int cal(point a,point b){
return abs(a.x+b.x)*abs(a.y+b.y)%mod;
}
int dfs(int st,int ed,int len){
if(len<=3) return 0;
if(dp[st][ed]!=-1) return dp[st][ed];
int ret = inf;
for(int i=st+1;i<ed;i++){
int tmp = dfs(st,i,i-st+1) + dfs(i,ed,ed-i+1);
if(i!=st+1) tmp += cal(p[st],p[i]);
if(i!=ed-1) tmp += cal(p[ed],p[i]);
ret = min(ret,tmp);
}
return dp[st][ed] = ret;
}
int main(){
while(scanf("%d%d",&n,&mod)!=EOF){
int flag = 1;
for(int i=0;i<n;i++) scanf("%d%d",&p[i].x,&p[i].y);
int cnt = Graham(p,n);
if(cnt<n){
puts("I can't cut.");
continue;
}
memset(dp,-1,sizeof(dp));
for(int i=n;i>=1;i--) p[i] = save[i-1];
int ans = dfs(1,n,n);
printf("%d\n",ans);
}
return 0;
}<strong>
</strong>
NO.2 LightOJ1422 Halloween Costumes
题意:你需要按照顺序参加n个party,每个party必须穿ai颜色的衣服,在参加一个party前,你可以脱下若干件(可以是0)或穿上1件衣服。脱下的衣服会被扔掉。问最少需要准备多少件衣服。
题解:dp[i][j]表示i到j区间的最优解
dp[i][j] = min(j-i+1,dp[i+1][j]+1,dp[i][j-1]+1)
if(a[i]==a[j]) dp[i][j] = dp[i][j-1]
dp[i][j] = min(dp[i][j] , dp[i][k]+dp[k+1][j]) only if a[k]==a[i]
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100+10;
const int inf = 1e9;
int dp[N][N];
int a[N];
int main(){
int t,cas=0,n;
cin >> t;
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++) dp[i][j] = j-i+1;
for(int len=2;len<=n;len++)
for(int i=1;i<=n-len+1;i++){
int j = i+len-1;
if(a[i]==a[j]) dp[i][j] = min(dp[i][j],dp[i][j-1]);
for(int k=i;k<j;k++)
if(a[i]==a[k])dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]);
}
printf("Case %d: %d\n",++cas,dp[1][n]);
}
return 0;
}<strong>
</strong>
NO.3 POJ2955 Brackets
题意:最长合法表达式的长度,(),【】是合法的。
题解:dp[i][j] = max(dp[i+1][j],dp[i][j-1])
dp[i][j] = dp[i+1][j-1]+2 only if a[i],a[j]合法
dp[i][j] = max(dp[i][k]+dp[k+1][j]) only if a[i],a[k]合法
总之就是很多转移的细节
代码:
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100+10;
const int N = maxn;
int dp[N][N],a[N];
char s[N];
int main(){
while(scanf("%s",s)!=EOF){
if(s[0]=='e') break;
int n = strlen(s);
for(int i=1;i<=n;i++){
if(s[i-1]=='(') a[i] = 1;
else if(s[i-1]==')') a[i] = 4;
else if(s[i-1]=='[') a[i] = 2;
else if(s[i-1]==']') a[i] = 3;
}
memset(dp,0,sizeof(dp));
int ans = 0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]+a[j]==5 && a[i]<a[j]) dp[i][j] = 2;
for(int len=2;len<=n;len++)
for(int i=1;i<=n-len+1;i++){
int j = i+len-1;
dp[i][j] = max(dp[i][j],dp[i+1][j]);
dp[i][j] = max(dp[i][j],dp[i][j-1]);
if(a[i]+a[j]==5 && a[i]<a[j]) dp[i][j] = max(dp[i][j],2+dp[i+1][j-1]);
for(int k=i+1;k<=j;k++)if(a[i]+a[k]==5&&a[i]<a[j])
dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++) ans = max(ans,dp[i][j]);
printf("%d\n",ans);
}
return 0;
}
</span>
NO.4 POJ1651 Multiplication Puzzle
题意:有n个数,选择一个删去2--n-1的数的一个顺序,使得消耗最小。删去一个数的消耗为a[l]*a[k]*a[r]
题解:dp[i][j]