[Offer收割]编程练习赛29

来源:互联网 发布:能以礼让为国乎 编辑:程序博客网 时间:2024/05/16 06:24

逃离迷宫4

题解

题目求解(a,b)出口,反向思维,由(a,b)(x,y)推导,发现仅有一条可行路径,如果不存在就输出1即可

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <unordered_set>#include <vector>#define FIN freopen("input.txt", "r", stdin)#define FOUT freopen("output.txt", "w", stdout)using namespace std;typedef long long LL;const int MAXN = 4e5 + 5;const int INF = 0x3f3f3f3f;bool flag;int x, y;void gcd(int a, int b) {    while(a != 0 && b != 0) {        if(a == x && b == y || b == x && a == y) {            flag = true;            break;        }        if(a > b) {            int t = a / b;            if(t > 2){                a = a - b * (t - 2);            }            a = a - b;        } else {            int t = b / a;            if(t > 2){                b = b - a * (t - 2);            }            b = b - a;        }    }}int a, b;int main() {    int _, a, b;    scanf("%d", &_);    while(_ --) {        scanf("%d%d%d%d", &x, &y, &a, &b);        flag = false;        gcd(a, b);        puts(flag ? "YES" : "NO");    }    return 0;}

最大得分

题解

由题目可以知道,总共存在最多1000个不同的数,然后是无序的,接着就可以列出如下状态转移方程:

dp[i][j]=dp[k][j1]+s[i]×count(s){s[i]s[k]>1}

其中i表示到了第几种数字,j表示已经选了多少数字,dp[i][j]则表示到了第i种数据,选了j种数字达到的最大得分,我们可以用map来维护数字和出现的个数,并且保证它有序,那么k的变化就不会超过2次,然后直接递推过去就可以得到答案了。

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <unordered_set>#include <vector>#include <map>#include <unordered_map>#define FIN freopen("input.txt", "r", stdin)#define FOUT freopen("output.txt", "w", stdout)using namespace std;typedef long long LL;const int MAXN = 1e3 + 5;const int INF = 0x3f3f3f3f;int N, M;int A[MAXN];int dp[MAXN][MAXN];map<int, int> Mii;vector<pair<int, int> > vpii;int main() {    while(~scanf("%d%d", &N, &M)) {        Mii.clear();        for(int i = 1; i <= N; i ++) {            scanf("%d", &A[i]);            Mii[A[i]] ++;        }        memset(dp, 0, sizeof(dp));        vpii.clear();        vpii.push_back(make_pair(-10, 0));        for(map<int, int>::iterator it = Mii.begin(); it != Mii.end(); it ++) {            vpii.push_back(make_pair(it -> first, it -> second));        }        for(int i = 1; i < vpii.size() ; i ++) {            for(int k = 1; k <= M; k ++) {                for(int j = i - 1; j >= 0; j --) {                    dp[i][k] = max(dp[i][k], dp[i - 1][k]);                    if(vpii[i].first - vpii[j].first > 1) {                        dp[i][k] = max(dp[i - 1][k], dp[j][k - 1] + vpii[i].first * vpii[i].second);                        break;                    }                }                //printf("%d, %d : [%d]\n", i, k, dp[i][k]);            }        }        printf("%d\n", dp[vpii.size() - 1][M]);    }    return 0;}

本质不同的回文子串的数量

题解

跟求不同回文子串的数目有点类似,不过这次不能统计相同的,所以我们同样用马拉车处理出回文子串的数目,然后用后缀数组处理出相邻的后缀的最长公共前缀,很明显拥有相同前缀的子串是会重复的,所以用一个临时变量维护已经计算过的子串长度,然后后面加的时候减去它就可以得到正确结果,个人用的模板是挑战程序设计竞赛上的后缀数组,因为非常好理解。

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <unordered_set>#include <vector>#include <map>#include <unordered_map>#define FIN freopen("input.txt", "r", stdin)#define FOUT freopen("output.txt", "w", stdout)using namespace std;typedef long long LL;const int MAXN = 8e5 + 5;const int INF = 0x3f3f3f3f;char S[MAXN], tS[MAXN << 1];int len[MAXN << 1], sz;int rank_[MAXN << 1], tmp[MAXN << 1], lcp[MAXN << 1], sa[MAXN << 1];int g_k;void mlc(){    sz = 0;    int ln = strlen(S);    tS[sz ++] = '$';    for(int i = 0;i < ln;i ++){        tS[sz ++] = '#';        tS[sz ++] = S[i];    }    tS[sz ++] = '#';    tS[sz] = '@';    int mx = 0, id = 0;    len[0] = 1;    for(int i = 1;i < sz;i ++){        if(mx > i){            len[i] = min(len[2 * id - i], mx - i);        }        else{            len[i] = 1;        }        while(tS[i + len[i]] == tS[i - len[i]]) len[i] ++;        if(len[i] + i > mx){            mx = len[i] + i;            id = i;        }    }}bool compare_sa(int i, int j){    if(rank_[i] != rank_[j]) return rank_[i] < rank_[j];    else {        int ri = i + g_k <= sz ? rank_[i + g_k] : -1;        int rj = j + g_k <= sz ? rank_[j + g_k] : -1;        return ri < rj;    }}void construct_sa(){    for(int i = 0;i <= sz;i ++){        sa[i] = i;        rank_[i] = i < sz ? tS[i] : -1;    }    for(g_k = 1;g_k <= sz;g_k <<= 1){        sort(sa, sa + sz + 1, compare_sa);        tmp[sa[0]] = 0;        for(int i = 1;i <= sz;i ++){            tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);        }        for(int i = 0;i <= sz;i ++){            rank_[i] = tmp[i];        }    }}void construct_lcp(){    for(int i = 0;i <= sz;i ++) rank_[sa[i]] = i;    int h = 0;    lcp[0] = 0;    for(int i = 0;i < sz;i ++){        int j = sa[rank_[i] - 1];        if(h > 0) h --;        for(;j + h < sz && i + h < sz;h ++){            if(tS[j + h] != tS[i + h]) break;        }        lcp[rank_[i] - 1] = h;    }}void solve(){    mlc();    construct_sa();    construct_lcp();    int t = 0, ans = 0;    for(int i = 1;i <= sz;i ++){        int f = sa[i];        t = min(t, lcp[i - 1]);        if(len[f] <= t) continue;        ans += (len[f] - t) / 2;        t = len[f];    }    printf("%d\n", ans);}int main(){    while(~scanf("%s", S)){        memset(len , 0, sizeof(len));        memset(rank_, 0, sizeof(len));        memset(tmp, 0, sizeof(tmp));        memset(lcp, 0, sizeof(lcp));        memset(sa, 0, sizeof(sa));        solve();    }}
原创粉丝点击