【牛客小白月赛4】A B C D[Floyd + 状压DP] E F[思维] H I J

A 三角形

分析:我们可以先简化一下问题,如果没有限制哪挑边不能够用,那么我们怎么求一个三角形的最大周长,很简单, sort一下, 然后从最高边权开始尺取每次枚举三条边 第一个可以构成三角形的就是最大周长。现在这里要问某些边不能够使用, 我们sort的时候记录每个边的id,就好了。这样如果有一条边不能够用,我们顺延就好了。
看代码吧

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = (int) 1e5 + 11;
const int M = (int) 1e6 + 11;

struct Ques{
    int id, a;
    Ques(){}
    Ques(int _id, int _a){
        id = _id; a = _a;
    }
}ques[N];
int n, q;
bool cmp(Ques a, Ques b){
    return a.a > b.a;
}
ll solve(int x){
    for(int i = 1; i + 2 <= n; i++){
        int a = i, b = i + 1, c = i + 2;
        if(ques[a].id == x){
            a++ ; b++; c++;
        }else if(ques[b].id == x){
            b++; c++;
        }else if(ques[c].id == x){
            c++;
        }
        if(c > n) continue;;

        if(ques[a].a < ques[b].a * 1ll + ques[c].a) {
            return ques[a].a * 1ll + ques[b].a + ques[c].a;  // 注意ll
        }
    }
    return -1;
}

int main(){
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; i++){
        int val; scanf("%d", &val);
        ques[i] = Ques(i, val);
    }

    sort(ques + 1, ques + 1 + n, cmp);
    while(q--){
        int x; scanf("%d", &x);
        printf("%lld\n", solve(x));
    }
return 0;
}
解法二,看到别人暴力也可以过,学习一波; 我只需要求出大多数询问的最大值,如果涉及到小部分,我们再重新暴力一次 代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = (int) 1e5 + 11;
const int M = (int) 1e5 + 11;
const int mod = (int) 1e9 + 7;
const int inf = 0x3f3f3f3f;



int a[N], Rank[N];
bool cmp(int x, int y){
    return a[x] > a[y];
}
bool vis[N]; ll ans[N];
int main(){
     int n, q; scanf("%d%d", &n, &q);
     for(int i = 0; i < n; i++) scanf("%d", &a[i]), Rank[i] = i;
     sort(Rank, Rank + n, cmp);
     ll MX = -1;
     for(int i = 0; i < min(n, 30); i++){
        vis[Rank[i]] = 1;
        for(int j = i + 1; j < min(n, 30); j++){
            for(int k = j + 1; k < min(n, 30); k++){
                int I = a[Rank[i]], J = a[Rank[j]], K = a[Rank[k]];
                if(I + J > K && I + K > J && K + J > I){
                    MX = max(MX, I * 1ll + J + K);
                }
            }
        }
        //printf("%lld\n", MX);
     }


     for(int o = 0; o < n; o++){
        if(!vis[o]) ans[o] = MX;
        else {
            ll MM = -1;
            for(int i = 0; i < min(n, 30); i++){
                if(o == Rank[i]) continue;
                for(int j = i + 1; j < min(n, 30); j++){
                    if(o == Rank[j]) continue;
                    for(int k = j + 1; k < min(n, 30); k++){
                    if(o == Rank[k]) continue;
                        int I = a[Rank[i]], J = a[Rank[j]], K = a[Rank[k]];
                        if(I + J > K && I + K > J && K + J > I){
                            MM = max(MM, I * 1ll + J + K);
                        }
                    }
                }
            }

            ans[o] = MM;
        }
     }

     while(q--){
        int val; scanf("%d", &val);
        printf("%lld\n", ans[val - 1]);
     }
return 0;
}

B 博弈论

分析: 很明显,最终的出现断层的位数一定不大。这样就足够了
代码

#include <bits/stdc++.h>
using namespace std;

const int N = (int) 1e3 + 11;

typedef long long ll;
int s[N + 1];

map<ll, bool>mp;
map<ll, bool>:: iterator it;
int main(){
    int n; scanf("%d" ,&n);
    for(int i = 0; i < n; i++) scanf("%d" ,&s[i]);

    for(int i = 1; i <= n && i <= 16 ; i++){
        for(int j = 0; j + i - 1 < n; j++){
            ll tmp = 0;

            for(int k = j; k <= j + i - 1; k++){
                tmp = tmp * 10 + s[k];
            }
            //printf("%lld \n", tmp);
            mp[tmp] = 1;
        }
    }

    ll id = 0;
    for(it = mp.begin(); it != mp.end(); it++){
        if(it->first != id) {
            break;
        }else id++;
    }
    printf("%lld\n", id);
return 0;
}

C 病菌感染

分析: 暴力
代码

#include <bits/stdc++.h>
using namespace std;

const int N = (int) 1e3 + 11;

bool mp[N][N];
int to [4][2] ={1, 0, -1, 0, 0, 1, 0, -1};
int main(){
    int n, m; scanf("%d%d", &n, &m);
    while(m--){
        int a, b; scanf("%d%d", &a, &b);
        mp[a][b] = 1;
    }
    int flag = 0;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            if(mp[i][j]) continue;
            int cnt = 0;
            for(int k = 0; k < 4; k ++){
                int x = i + to[k][0]; int y = j + to[k][1];
                if(x < 1 || y < 1 || x > n || y > n) continue;

                if(mp[x][y]) cnt ++;
                if(cnt >= 2){
                    flag++;
                    break;
                }
            }
        }
    }
    if(flag == n * n - m) puts("YES");
    else puts("NO");
return 0;
}

D 郊区春游

分析: 因为只和这R个景区有关,所以我们先预处理出来任意两个点之间的最短距离,然后就是典型的TSP问题, 记忆化搜索一下。
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = (int) 1e3 + 11;
const int M = (int) 1e5 + 11;
const int mod = (int) 1e9 + 7;
const int inf = 0x3f3f3f3f;


int dis[N][N];
void init(int n){
    for(int i = 0; i <= n; i++){
        for(int j = 0; j <= n; j++){
            if(i == j) dis[i][j] = 0;
            else dis[i][j] = dis[j][i] = inf;
        }
    }
}

void floyd(int n){
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
}

int pos[N], dp[1 << 16][16]; int r;
int TSP(int s, int v){  // 已经走过的状态为s, 并且当前在v位置
    if(dp[s][v] != -1) return dp[s][v];
    if(s == (1 << r) - 1) return dp[s][v] = 0;

    int res = inf;
    for(int i = 0; i < r; i++){
        if(!((s >> i) & 1)){
            res = min(res, TSP(s | (1 << i), i) + dis[pos[v]][pos[i]]);
        }
    }

    return dp[s][v] = res;
}

int main(){

    int n, m; scanf("%d%d%d", &n,&m, &r);
    init(n);
    for(int i = 0; i < r; i++) scanf("%d", &pos[i]);
    while(m--){
        int a, b, c; scanf("%d%d%d", &a, &b, &c);
        dis[a][b] = dis[b][a] = min(dis[a][b], c );
    }
    floyd(n);
    memset(dp, -1, sizeof(dp));
    int ans = inf;
    for(int i = 0; i < r; i++) // 因为不确定起点,所以我们遍历所有
        ans = min(ans, TSP(1 << i, i));

    printf("%d\n", ans);
return 0;
}

E 浮点数输出

代码

#include <bits/stdc++.h>
using namespace std;


int main(){
    string s; cin>>s;
    cout<<s;


return 0;
}

F 等价串

分析: 哇,自己想的真难想啊,其实只要想对方向,这题真的挺简单。如果A串可以到达B串,那么说明 A串在原串的情况下 全部变为0,和B串也全部变为0,本质上应该是一样的,因为三个0可以删除掉,所以只要模三后相同就足够了
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = (int) 1e3 + 11;
const int M = (int) 1e5 + 11;
const int mod = (int) 1e9 + 7;
const int inf = 0x3f3f3f3f;


char A[N], B[N];
int change(char *s){
    int cnt = strlen(s);
    for(int i = 0; s[i]; i++){
        if(s[i] == '1') cnt ++;
    }
    return cnt;
}

int main(){
    int T; scanf("%d", &T);
    while(T--){
        int n, m; scanf("%d%d" ,&n, &m);
        scanf("%s%s", A, B);
        bool flag = change(A) % 3 == change(B) % 3;
        puts(flag ? "YES" : "NO");
    }
return 0;
}

H 相邻的糖果

分析:尺取的去维护 每m堆的值。
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = (int) 1e6 + 11;
const int M = (int) 1e5 + 11;
const int mod = (int) 1e9 + 7;


int a[N];
int main(){
    int n, m, x; scanf("%d%d%d", &n, &m, &x);
    for(int i = 0; i < n; i++){
        scanf("%d", &a[i]);
    }

    ll ans = 0; ll sum = 0;
    for(int i = 0; i < m; i++)  sum += a[i];
    int id = m - 1;
    while(sum > x){  //  先预处理好前m项,贪心的最后一位开始减少值
        if(sum - a[id] >= x){
            sum -= a[id];
            ans += a[id];
            a[id] = 0;
        }
        else {
            a[id] -= sum - x;
            ans += sum - x;
            sum = x;
        }
        id--;
    }


    for(int i = m; i < n; i++){
        sum += a[i];
        sum -= a[i - m];
        if(sum > x){ // 如果一旦出现大于x的情况,那么一定是由于加上a[i]导致的
            ans += sum - x;
            a[i] -= sum - x;
            sum = x;
        }
    }

    printf("%lld\n", ans);

return 0;
}

I 合唱队形

好像写的复杂了
代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = (int) 1e5 + 11;

int pos[N];
int n;
void get(int id, int &l, int &r){// 从id位开始,第一个连续的女生左右区间
    l = r = id;
    while(id < n && pos[id]) id ++;
    if(id < n) {
        l = id;
        while(id < n && !pos[id]) id ++;
        if(id < n) {
            r = id - 1;
        }else r = n - 1;

    }else l = -1;
}

int main(){
    scanf("%d" ,&n);
    string s; cin >>s;

    int ge = 0;// 男生个数
    for(int i = 0; i < n; i++){
        pos[i] = s[i] - '0';
        ge += pos[i];
    }

    int mx = 0; int cnt = 0;
    for(int i = 0; ; ){
        int l, r;
        get(i, l, r);
        if(l == -1) break;
        mx = max(mx, r - l + 1);
        if(r - l + 1 != n - ge) mx = max(mx, r - l + 2);
        i = r + 1;
    }

    int a, b, c, d;

    get(0, a, b);
    if(a != -1)
    for(int i = b + 1; ; ){
        get(i, c, d);
        if(c == -1) break;

        if(b + 2 == c){
            int dis = (b - a + 1) + (d - c + 1);
            mx = max(mx, dis);
            if(dis < n - ge)  mx = max(mx, dis + 1);
        }
        a = c;  b = d ;
        i = d + 1;
    }

    printf("%d\n", mx);

return 0;
}

J 强迫症

分析: 每次把目前最大的数加到一个重复的数上

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = (int) 1e5 + 11;


map<int, int>mp;
map<int, int>::iterator it;
int main(){
    int n; scanf("%d" ,&n);
    for(int i = 0; i < n; i++){
        int val; scanf("%d" ,&val);
        mp[val] ++;
    }
    int ans = 0;
    for(it = mp.begin(); it != mp.end(); it++){
        ans += it->second - 1;
    }
    printf("%d\n", ans);
return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37383726/article/details/80755163