zoj 2860 Breaking Strings

Breaking Strings

Time Limit: 2 Seconds      Memory Limit: 65536 KB

A certain string-processing language allows the programmer to break a string into two pieces. Since this involves copying the old string, it costs n units of time to break a string of n characters into two pieces. Suppose a programmer wants to break a string into many pieces. The order in which the breaks are made can affect the total amount of time used. For example, suppose we wish to break a 20 character string after characters 3, 8, and 10 (numbering the characters in ascending order from the left-hand end, starting from 1). If the breaks are made in left-to-right order, then the first break cost 20 units of time, the second break costs 17 units of time, and the third breaks costs 12 units of time, a total of 49 units of time (see the sample below). If the breaks are made in right-to-left order, then the first break costs 20 units of time, the second break costs 10 units of time, and the third break costs 8 units of time, a total of 38 units of time.

The cost of making the breaks in left-to-right order:

thisisastringofchars     (original)
thi sisastringofchars    (cost:20 units)
thi sisas tringofchars   (cost:17 units)
thi sisas tr ingofchars  (cost:12 units)
                         Total: 49 units.

The cost of making the breaks in right-to-left order:

thisisastringofchars     (original)
thisisastr ingofchars    (cost:20 units)
thisisas tr ingofchars   (cost:10 units)
thi sisas tr ingofchars  (cost: 8 units)
                         Total: 38 units.

Input:

There are several test cases! In each test case, the first line contains 2 integers N (2<=N<=10000000) and M (1<=M<=1000, M<N). N is the original length of the string, and M is the number of the breaks. The following lines contain M integers Mi (1<=Mi<N) in ascending order that represent the breaking positions from the string's left-hand end.

Output:

For each test case, output in one line the least cost to make all the breakings.

Sample Input:

20 3
3 8 10

Sample Output:

37

Author: Wei, Qizheng

Source: ZOJ Monthly, June 2007

    开始看到题目给的n发现数据量巨大如果用dp的话应该开不下。纠结一会儿后茅塞顿开。可以根据分割点来dp.若有m个分割点。那么整个字符串就被分割成0-1-2-3-.....-m-m+1个点而要分割的点在0-m+1之间。那么定义dp[i][j]为把i到j内的分割点分割好的最小花费。那么转移方程就显而易见了。

dp[i][j]=dp[i][k]+dp[k][j]+cost[i][j].注意是k不是k+1因为若是k+1若k+1是分割点dp[k+1][j]就没包含分割k+1点的价值了。

cost[i][j]=a[j]-a[i]即区间长度。

根据cost[i][j]的结构显然满足包含单调性。

设i'<i<j<j'。则有a[i']<a[i]<a[j]<a[j']。

设a[i']=a。a[i]=a+b。a[j]=a+b+c。a[j']=a+b+c+d。

cost[i'][j]=b+c。cost[i][j']=c+d。cost[i][j]=c。cost[i'][j']=b+c+d。

所以cost[i'][j]+cost[i][j']<=cost[i'][j']+cost[i][j].

所以满足四边形不等式。

所以k的枚举范围就变为s[i][j-1]<=k<=s[i+1][j]。缩小的枚举范围复杂度降到了o(n^2)。

#include <iostream>
#include<stdio.h>
#include<string.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define positive(a) ((a)>0?(a):-(a))
using namespace std;
int dp[1010][1010],cost[1010][1010],s[1010][1010],a[1010];
int n,m;

void solve()
{
    int i,j,k,l,r,len,temp;

    for(len=3;len<=m+1;len++)//枚举区间长度。保证可递推
    {
        for(i=0;i+len-1<=m;i++)//枚举i从大到小从小到大都可以
        {
            j=i+len-1;
            l=s[i][j-1];//左边界
            r=s[i+1][j];//右边界
            for(k=l;k<=r;k++)//在区间内枚举k
            {
                temp=dp[i][k]+dp[k][j]+cost[i][j];
                if(temp<dp[i][j])
                {
                    dp[i][j]=temp;
                    s[i][j]=k;
                }
            }
        }
    }
}
int main()
{
    int i,j;

    while(~scanf("%d%d",&n,&m))
    {
        for(i=1;i<=m;i++)
            scanf("%d",a+i);
        a[0]=0;a[i]=n;
        m++;//有m个分割点的话端点为m+1
        memset(dp,0x3f,sizeof dp);
        for(i=0;i<m;i++)//分割区间的花费为区间长度
            for(j=i+1;j<=m;j++)
               cost[i][j]=a[j]-a[i];
        for(i=0;i<=m;i++)
        {
            s[i][i+1]=i+1;//区间长度为一的枚举范围为i到i+1
            dp[i][i+1]=0;//区间长度为1时不用分割花费为0
            cost[i][i+1]=0;
        }
        solve();
        printf("%d\n",dp[0][m]);
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值