usaco5.1.3 Musical Themes

来源:互联网 发布:按键精灵抢红包源码 编辑:程序博客网 时间:2024/06/03 23:41

一 原题

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)

3025 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 1882 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] 


二 分析

要求在一个长度最大为5000的串L中找到两个不相交的子串l1,l2,满足两个条件:(1)l1,l2长度相同(2)对应位置上的元素差为定值。
很容易想到把L中每个元素与前一个元素做差。我们只要求新数组中两个完全相同的子串就好了(有些处理不相交的细节注意一下)。
第一个想法就是:二分答案,枚举所有长度为二分值的字串与原串做kmp。这样的复杂度是O(n*n*lgn)。结果TLE
第二个想法是动规,其实很好想:dp[i][j]表示以i,j结尾的最长匹配子串长度,复杂度O(n*n)。结果爆内存一次,没输出0又WA一次。。但使用滚动数组是可以过的

三 代码

代码1(二分+KMP):
/*ID:maxkibb3LANG:C++PROB:theme*/#include<cstdio>#include<vector>using namespace std;const int MAX = 5005;const int IMPOSSIBLE_NUM = 89;int n;int a[MAX];int b[MAX];vector<int> c;int nxt[MAX] = {-1, 0};void init() {    scanf("%d", &n);    for(int i = 0; i < n; i++) {        scanf("%d", &a[i]);        if(i == 0) continue;        b[i] = a[i] - a[i - 1];    }}void set_next(int _i) {    int idx = nxt[_i - 1];    bool match = false;    while(idx != -1) {        if(c[_i - 1] != c[idx]) {            idx = nxt[idx];            continue;        }        match = true;        nxt[_i] = idx + 1;        break;    }    if(!match) nxt[_i] = 0;}int kmp() {    int ans = -1;    for(int i = 2; i <= c.size(); i++)        set_next(i);    for(int i = 1; i < n;) {        for(int j = 0; j < c.size();) {            if(b[i] == c[j]) {                i++;                j++;            }            else if(j == 0)                i++;            else                j = nxt[j];            if(j == c.size() - 1) ans = i - 1;            if(i == n) break;        }    }    return ans;}bool judge(int _len) {    for(int i = 1; i <= n - _len; i++) {        c.clear();        for(int j = 0; j < _len; j++)            c.push_back(b[i + j]);        c.push_back(IMPOSSIBLE_NUM);        int idx = kmp();        if(idx - i < 2 * _len) continue;        else return true;    }    return false;}void solve() {    int l = 4, r = n / 2;    int ans;        if(!judge(4)) {        printf("0\n");        return;    }         while(l < r) {        int mid = (l + r) / 2;        if(r - l == 1) mid = r;        if(judge(mid))            l = mid;        else            r = mid - 1;    }    printf("%d\n", l + 1);}int main() {    freopen("theme.in", "r", stdin);    freopen("theme.out", "w", stdout);    init();    solve();    return 0;}



运行结果:
USER: Qi Shen [maxkibb3]TASK: themeLANG: C++Compiling...Compile: OKExecuting...   Test 1: TEST OK [0.000 secs, 4256 KB]   Test 2: TEST OK [0.000 secs, 4256 KB]   Test 3: TEST OK [0.000 secs, 4256 KB]   Test 4: TEST OK [0.000 secs, 4256 KB]   Test 5: TEST OK [0.000 secs, 4256 KB]   Test 6: TEST OK [0.000 secs, 4256 KB]   Test 7: TEST OK [0.000 secs, 4256 KB]   Test 8: TEST OK [0.000 secs, 4256 KB]   Test 9: TEST OK [0.011 secs, 4256 KB]   Test 10: TEST OK [0.000 secs, 4256 KB]   Test 11: TEST OK [0.108 secs, 4256 KB]   Test 12: TEST OK [0.076 secs, 4256 KB]   Test 13: TEST OK [0.086 secs, 4256 KB]   Test 14: TEST OK [0.097 secs, 4256 KB]   Test 15: TEST OK [0.086 secs, 4256 KB]All tests OK.

Your program ('theme') produced all correct answers! This is yoursubmission #5 for this problem. Congratulations!


AC代码1(动态规划):
/*ID:maxkibb3LANG:C++PROB:theme*/#include<cstdio>#include<algorithm>using namespace std;const int MAX = 5005;int n, a[MAX];int b[MAX];int dp[2][MAX];int ans;int main() {    freopen("theme.in", "r", stdin);    freopen("theme.out", "w", stdout);    scanf("%d", &n);    for(int i = 0; i < n; i++) {        scanf("%d", &a[i]);        if(i == 0) continue;        b[i] = a[i] - a[i - 1];    }        for(int i = 1; i < n; i++) {        for(int j = i + 1; j < n; j++) {            if(b[j] == b[i]) {                dp[1][j] = min(dp[0][j - 1] + 1,                        j - i - 1);                if(dp[1][j] > ans) ans = dp[1][j];            }            else dp[1][j] = 0;        }        for(int j = 0; j < n; j++)            swap(dp[0][j], dp[1][j]);    }    if(ans < 4) printf("0\n");    else printf("%d\n", ans + 1);    return 0;}


0 0