您的位置:首页 > 其它

2017 北京区域赛 J题

2017-11-20 21:05 232 查看
J Pangu and Stones

题意:有n个数字,每次可以选择将k个连续的数字合并成一个数,k∈[l,r],每次合并的代价是合并数的和。问将所有数合并成一个数的最小代价和是多少,如果不能全部合并成一个数,就输出0;

样例:

input:

3 2 2

1 2 3

output:

9

hint:

3个数字,l=2,r=2;

3个数字分别是1,2,3

合并两次

1,2合并为3,代价3

3 3合并成6 代价为3+6=9

分析

区间dp;

这种典型连续问题就是dp吖;

感觉难点是状态要找对..

开始想的是dp[i][j][k]:表示每次合并k,下标从i到j的数字合并起来的代价

然后不会写状态转移了

但是换一个想法,dp[i][j][k]:表示下标从i到j的数字可以分成k堆。

状态转移就好写多了~

dp[i][j][k]=dp[i][j][t]+dp[t+1][j][1];

因为要由已知推未知,所以这里的循环顺序不要搞反了..

using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn = 150;
int dp[maxn][maxn][maxn];
int sum[maxn];
int main()
{
int n,l,r;
while(scanf("%d %d %d",&n,&l,&r)!=EOF)
{
mem(dp,-1);mem(sum,0);
for(int i=1;i<=n;i++)
{
scanf("%d",&sum[i]);
sum[i]+=sum[i-1];
}
for(int len = 1;len<=n;len++)
{
for(int i=1;i<=n;i++)
{
int j = i+len-1;
if(j>n) <
4000
span class="hljs-keyword">break;
dp[i][j][len]=0;
for(int k=len-1;k>=2;k--)
{
for(int t=i;t<j;t++){
if(dp[i][t][k-1]!=-1&&dp[t+1][j][1]!=-1){
if(dp[i][j][k]==-1) dp[i][j][k]=dp[i][t][k-1]+dp[t+1][j][1];
else dp[i][j][k]=min(dp[i][j][k],dp[i][t][k-1]+dp[t+1][j][1]);
}
}
}
for(int k=l;k<=r;k++)
{
if(dp[i][j][k]!=-1){
if(dp[i][j][1]==-1) {dp[i][j][1]=dp[i][j][k]+sum[j]-sum[i-1];continue;}
dp[i][j][1]=min(dp[i][j][1],dp[i][j][k]+sum[j]-sum[i-1]);
}
}
}
}
if(dp[1]
[1]==-1) printf("0\n");
else printf("%d\n",dp[1]
[1]);
}
return 0;
}
/*
7 3 3
1 1 1 5 1 1 1
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: