0
点赞
收藏
分享

微信扫一扫

Halloween Costumes  LightOJ - 1422 ​​​​​​​ 区间DP 两种做法


Halloween Costumes

​​ LightOJ - 1422 ​​

算法分析:

题意:

有N个宴会,对于每一个宴会都有对应的一种礼服,一个人都要穿一种礼服,脱了的不能再用,需要新买一件,礼服可以套着穿,参加宴会必须按顺序来,从第一个到第N个,问参加这些宴会最少需要几件礼服。

分析:

要不是老师给的区间DP,还真不知道从区间DP下手,但好像这种选择有影响到后面的状态,好像一般都用区间DP。

好,我们知道是区间DP了,dp[i][j]自然表示从i到j至少穿的衣服数量,我们如何优化呢,

我们先假设选择a[j]衣服,我们什么时候不用选择a[j]衣服呢,那就是断点k,如果有a[k]==a[j],那我们就不用选了,就需要重新比较了,得到状态转移方程,dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]),注意到j-1,不选择a[j]这件衣服;

阶段:区间长度

状态:枚举区间左端点

决策:间断点k

状态转移方程:

dp[i][j]=dp[i][j-1]+1;

if(a[j]==a[k])   //k有同一件

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]);

边界条件:dp[i][i]=1;

看网上还有更简单的答案,倒着枚举,两重循环即可,不过已经很开心,第一次区间DP可以做出了。

代码实现:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int dp[1000][1000],a[1000];
int main()
{
int t;
cin>>t;
int q=0;
while(t--)
{
q++;
int n;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
dp[i][i]=1;
}
for(int len=1;len<=n;len++) //区间长度
for(int i=1;i<=n;i++) //枚举起点
{
int j=i+len-1; //区间终点
dp[i][j]=dp[i][j-1]+1;
if(j>n) break; //越界结束
for(int k=i;k<j;k++) //枚举分割点,构造状态转移方程
{
if(a[j]==a[k]) //k有同一件
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]);

}

}
printf("Case %d: %d\n",q, dp[1][n]);

}
}

第二种方法:

另一种做法,很巧妙的简化成二重循环,但基本思想一样

/*
* Light OJ 1422 - Halloween Costumes
* http://lightoj.com/volume_showproblem.php?problem=1422
* 区间DP的思想。
* 比如要求解i到j,dp[i][j].
* 就是考虑i的衣服,一种是i的衣服只有在i使用,那么就是dp[i+1][j]+1件
* 然后再i+1~j中枚举k,如果a[i]==a[k].那么dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j])
* 注意因为i的衣服是可以使用多次的,所以不需要加1,是dp[k][j]
* 思想很妙
*/

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAXN=110;
int a[MAXN];
int dp[MAXN][MAXN];

int main()
{
int T;
int n;
scanf("%d",&T);
int iCase=0;
while(T--)
{
iCase++;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
dp[i][j]=j-i+1;
for(int i=n-1;i>=1;i--)//注意DP的顺序
for(int j=i+1;j<=n;j++)
{
dp[i][j]=dp[i][j - 1]+1;//这个表示第i个的衣服在后面没有利用了
for(int k=i;k<=j - 1;k++)
if(a[j]==a[k])//用同一件
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k + 1][j - 1]);
}
printf("Case %d: %d\n",iCase,dp[1][n]);
}
return 0;
}

//3.16重做
#include<cstdio>
#include<iostream>
#include<fstream>
#include<algorithm>
#include<functional>
#include<cstring>
#include<string>
#include<cstdlib>
#include<iomanip>
#include<numeric>
#include<cctype>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<list>
#include<set>
#include<map>
using namespace std;
using namespace std;
int a[10005],dp[10005][105];
int f(int t,int mod)
{

while(t<0) t+=mod;
return t%mod;
}
int main()
{
int n,k;
int t;
scanf("%d",&t);
int cas=0;
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i]=1;
}

for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
dp[l][r]=dp[l][r-1]+1;
for(int k=l;k<r;k++)
{
if(a[r]==a[k])
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]-1);
}
}
}
printf("Case %d: %d\n",++cas, dp[1][n]);

}
return 0;
}

 

举报

相关推荐

0 条评论