usaco 5.1 Musical Themes(KMP或DP)

该问题要求在给定的钢琴键序列中找到最长的重复音乐主题,即至少五音符长且在序列中非重叠地重复出现的子序列。可以使用动态规划(DP)方法解决,通过计算不同起点的重复子序列长度来找到最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Musical Themes
Brian Dean

A musical melody is represented as a sequence of N (1 <= N <= 5000) notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of musical timing; but, this programming task is about notes and not timings.

Many composers structure their music around a repeating "theme", which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it:

  • is at least five notes long
  • appears (potentially transposed -- see below) again somewhere else in the piece of music
  • is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s)
Transposed means that a constant positive or negative value is added to every note value in the theme subsequence.

Given a melody, compute the length (number of notes) of the longest theme.

One second time limit for this problem's solutions!

PROGRAM NAME: theme

INPUT FORMAT

The first line of the input file contains the integer N. Each subsequent line (except potentially the last) contains 20 integers representing the sequence of notes. The last line contains the remainder of the notes, potentially fewer than 20.

SAMPLE INPUT (file theme.in)

30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80

OUTPUT FORMAT

The output file should contain a single line with a single integer that represents the length of the longest theme. If there are no themes, output 0.

SAMPLE OUTPUT (file theme.out)

5

[The five-long theme is the last five notes of the first line and the first five notes of the second] 

题意:给你一个数列,问数列中差值关系相同的两个子串的最大长度

分析:一开始想到的办法就是把相邻的两个数相减,那么只要找到差值序列的两个相同子序列就行

然后就是枚举一个子序列,用KMP找另一个子序列,最后看题解发现这种方法弱爆了T_T

题解的方法是DP

如下

设f[i,j]为两段重复旋律的开头为i和j时的最大长度。

则:f[i,j]=min(f[i-1,j-1]+1,i-j)   当s[i]==s[j]时

              =1                                当s[i]<>s[j]时


DP方法:

/*
ID: 15114582
PROG: theme
LANG: C++
*/
#include<cstdio>
#include<iostream>
using namespace std;
const int mm=5555;
int a[mm],s[mm],f[mm];
int i,j,k,n,ans;
int main()
{
    freopen("theme.in","r",stdin);
    freopen("theme.out","w",stdout);
    while(~scanf("%d",&n))
    {
        for(i=0;i<n;++i)
            scanf("%d",&a[i]);
        for(i=1;i<n;++i)
            s[i]=a[i-1]-a[i];
        ans=0;
        for(i=1;i<n;++i)
            for(j=n-1;j>i+ans;--j)
            {
                if(s[i]==s[j])f[j]=min(f[j-1]+1,j-i+1);
                else f[j]=0;
                ans=max(ans,f[j]+1);
            }
        if(ans<5)ans=0;
        printf("%d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值