http://vjudge.net/contest/77874#overview
kuangbin 带你飞-专题22 区间dp
我是没做完,感觉简单的会了,深入留着给今年比赛打完再学吧。
poj 2955
最大矩阵连乘数
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
char str[105];
/*
a*b 和 b*c 的矩阵相乘
乘法次数是a*b*c次 ,形成一个a*c的矩阵,
这题的样例的意思是:从i=2开始
f[i-1]*f[i] 组成 第i个矩阵;
3
10 100 5 50
其中Ai与Ai+1是可乘的 (同理 Ai和Ai-1可乘 )
我们设 从 i,j 最大的乘法次数dp[i][j]
因为最后肯定变成了一个矩形,肯定是两个矩阵相乘,又矩形都只能和左右两边相乘
所以这就形成了一个区间, i....k 和 k+1....j , 最后必然这种情况。
所以 我们对于[i,j]这个区间 遍历所有的k ,找到最大值,就可以了。
而 题目中必然存在多个区间 [1,n] [2,n-1],[3,n-1] 等等
我们可以用 dfs 解决之
最后又感叹一句:
我真是连dfs都不会的人
*/
int f[15];
int p[15],q[15];
int dfs(int x,int y){
int ans=0;
if(x==y) return 0;
for(int i=x;i<y;i++){
int temp=dfs(x,i)+dfs(i+1,y)+p[x]*q[i]*q[y];
// 不管这个函数里面如何去乘 ,
/*
a b .... x. y.....f
b c .... y..z.....h
比如第i个矩阵 是x*y , 最后 必然形成两个矩阵:a*y 和 y*h
而这两个矩阵 相乘次数 就是 a*y*h 即p[x] * q[i]*q[y]
*/
ans=max(ans,temp);
}
return ans;
}
int main(){
freopen("1.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++){
scanf("%d",&f[i]);
if(i>=1){
p[i]=f[i-1];
q[i]=f[i];
}
}
printf("%d\n",dfs(1,n));
}
return 0;
}
题意:
告诉有n场晚会中需要穿的衣服,衣服是可以套在其他衣服外面的,也就是说如果顺序为 1 2 1,那么可以将2套在1外面,第三场晚会需要穿1的时候把2脱掉即可,这样就只需要穿两次衣服。
题目是再告诉了顺序之后需要求出在某种序列下最少需要穿多少次衣服。
样例 1 2 1 2: ①穿1 ②穿2 ③脱2 ④穿2 或者 ①穿1 ②脱1穿2 ③穿1 ④脱1 均输出 3
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
int c[105];
int dp[105][105];
/*
dp[i][j] : i~j 最少穿衣次数 dp[i][i]=1; 最开始假设每个舞会都穿一件衣服
if j+1=j dp[i][j+1]=dp[i][j];
if i-1=i dp[i-1][j]=dp[i][j];
=》
if i==j dp[i][j]=dp[i][j-1] // 如果前面有 那就只需要脱下
还是原来的套路
区间 区间,无非就是在 [i,j]中间 设置k 点 一步步求最值
[i,k][k+1,j]
假设知道i,k 最值 以及 k+1,j最小 。
那么 可能多算了,因为i,k 身上至少是有衣服的,k+1-j 默认k+1是没有衣服的。
所以应该减什么呢? 这不能算出需要减多少,所以我们应该找个特殊情况
if i==k 则我们知道i-k 过程中穿的衣服都脱下来了
dp[i][j]=dp[i][k] + dp[k+1][j];
但是如果 k+1~j 中再出现 c[pos]=c[i] ,那么我们让 k=pos, dp[1][pos]+dp[pos+1][j];
所以:总能找到那个最小值。
*/
int dfs(int l,int r){
// printf("l r %d %d %d\n",l,r,dp[l][r]);
if(dp[l][r]!=-1){
return dp[l][r];
}
if(l==r){
return dp[l][r]=1;
}
if(l>r){
return 0;
}
int ans=1000;
for(int k=l;k<=r;k++){
if(c[k]==c[l]){
if(k!=l) //必然要等dfs遍历完成
dp[l][k]=dfs(l,k-1); //if k==l dp[l][l]=dp[l][l-1]=0
ans=min(ans, dfs(l,k)+dfs(k+1,r) );
dp[l][r]=ans;
}
}
return ans;
}
int main(){
//freopen("1.txt","r",stdin);
int t;
scanf("%d",&t);
int cas=1;
while(t--){
printf("Case %d: ",cas++);
int n;
memset(dp,-1,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
dp[i][i]=1;
}
printf("%d\n",dfs(1,n));
}
return 0;
}
意:
给出两个串s1和s2,一次只能将一个区间刷一次,问最少几次能让s1=s2
例如zzzzzfzzzzz,长度为11,我们就将下标看做0~10
zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd
Sample Output
6
7
先将0~10刷一次,变成aaaaaaaaaaa
1~9刷一次,abbbbbbbbba
2~8:abcccccccba
3~7:abcdddddcba
4~6:abcdeeedcab
5:abcdefedcab
这样就6次,变成了s2串了
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
char s1[105],s2[105];
int dp[110][110];
int len;
/*
dp[i][j] 代表从i-j 需要最少的次数
老规矩: 找一个优化
if i==j 则 dp[i][j] = dp[i+1][j-1] + 1
if i==i+1 dp[i+1][j]= dp[i+2][j];
然后开始 k 开始循环
dp[i][k] + dp[k+1][j]
i-k 最小的次数 + k+1 - j 的最小次数
这必然会有错,因为做i-k时有可能连k+1-j 也做过一些;
找一些可以解除这些错误的情况
=》 s2[i]==s2[k] 也是要找最后一个k
dp[i][j] = dp[i+1][k-1] + 1 + dp[k+1][j];
if s1[i]=s2[i]
dp[i][j]=dp[i+1][j]
*/
//int dfs(int l,int r){
// if(dp[l][r]!=-1)
// return dp[l][r];
// if(l==r)
// return 1;
// if(l>r)
// return 0;
//
// int ans=1000;
// for(int k=l+1;k<=r;k++){
// ans=dfs(l+1,k)+1; //初始化
// if(s2[l]==s2[k]){
printf("l k : %d %d\n",l,k);
// ans=min(ans, dfs(l+1,k) + dfs(k+1,r) ); //AAA=2;
// }
// }
// dp[l][r]=ans;
// printf("%d %d=%d\n",l,r,dp[l][r]);
// return dp[l][r];
//}
int main(){
//freopen("1.txt","r",stdin);
while(~scanf("%s %s",s1,s2)){
len=strlen(s1);
memset(dp,0,sizeof(dp));
for(int l=0;l<len;l++) //扩大长度
for(int i=l;i>=0;i--){ //策略找的方向就有问题
dp[i][l]=dp[i+1][l]+1; //按题意来的初始化
for(int k=i+1;k<=l;k++) //这里记得是j
if(s2[i]==s2[k])
dp[i][l]=min(dp[i][l],dp[i+1][k]+ dp[k+1][l]);
}
// printf("%d\n",dp[0][len-1]);
for(int i=0;i<len;i++){ //s1 和 s2 相同 还没有处理,这个放在dfs里面去处理感觉很年轻
if(s1[i] == s2[i]){
dp[0][i]=dp[0][i-1];
}
else
for(int j=0;j<i;j++){ //同理也是找k , 但显得很粗暴。。。根本不在乎是否相同
dp[0][i]=min(dp[0][i], dp[0][j] + dp[j+1][i]);
}
}
printf("%d\n",dp[0][len-1]);
}
}