[Offer收割]编程练习赛29

逃离迷宫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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值