牛客每日一題系列(持續更新)

牛客每日一題系列(持續更新)

ps

供自己複習、記錄題解所用,如有錯誤概不負責(滑稽)

一 tokitsukaze and Soldier

題意:
n個士兵,需要從中選出一些士兵,但是每個士兵都有對應的戰力值v和對人數的限制s
求:選出的士兵,最多能形成多大的戰力值

題解:
我們需要維護選出的士兵形成的集合中對人數的限制,需要使得所有的限制中最小值要儘可能的大,我們可以對每個士兵對人數限制那個參數進行從大到小排序,每次選擇還沒有進隊列中的最大的那個s,使其進入隊列中

至於每個士兵的戰力值這個參數,我們需要滿足他們的和儘可能的大,也就是每當人數不符合要求時,我們將小的值移出集合
於是可以採用用**堆(即:優先隊列)**來進行維護
(這是看的題解,當時沒想出來)

代碼:

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const ll mod=1e9+7;
const int maxn=1e5+5;
int n;
struct node{
    int v,s;
};
node a[maxn];
priority_queue<int,vector<int>,greater<int> >q;
bool cmp(node x,node y){
    return x.s>y.s;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].v,&a[i].s);
    }
    sort(a+1,a+n+1,cmp);
    ll ans=0,temp=0;
    for(int i=1;i<=n;i++){
        q.push(a[i].v);
        temp+=a[i].v;
        while(q.size()>a[i].s){
            temp-=q.top();
            q.pop();
        }
        ans=max(ans,temp);
    }
    printf("%lld\n",ans);
    return 0;
}

二 合併迴文子串

題意:
給定兩個字符串,求合併後能獲得的最長迴文子串

題解:
一個比較難的dp問題,首先dp數組dp[i][j][k][l]
表示:字符串a:i~j ;字符串b:k~l
然後:

if(len1>1&&a[i]==a[j]) dp[i][j][k][l]+=dp[i+1][j-1][k][l];
if(len2>1&&b[k]==b[l]) dp[i][j][k][l]+=dp[i][j][k+1][l-1];
if(len1&&len2&&a[i]==b[l]) dp[i][j][k][l]+=dp[i+1][j][k][l-1];
if(len1&&len2&&a[j]==b[k]) dp[i][j][k][l]+=dp[i][j-1][k+1][l];

注意dp數組的初始化。

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1000;
const ll mod=998244353;
int t;
char a[55],b[55];
int dp[110][110][110][110];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%s",a+1);
        scanf("%s",b+1);
        int n=strlen(a+1);
        int m=strlen(b+1);
        int ans=0;
        for(int len1=0;len1<=n;len1++){
            for(int len2=0;len2<=m;len2++){
                for(int i=1;i<=n-len1+1;i++){
                    for(int k=1;k<=m-len2+1;k++){
                        int j=i+len1-1,l=k+len2-1;
                        if(len1+len2<=1){
                            dp[i][j][k][l]=1;
                        }
                        else{
                            dp[i][j][k][l]=0;
                            if(len1>1&&a[i]==a[j]) dp[i][j][k][l]+=dp[i+1][j-1][k][l];
                            if(len2>1&&b[k]==b[l]) dp[i][j][k][l]+=dp[i][j][k+1][l-1];
                            if(len1&&len2&&a[i]==b[l]) dp[i][j][k][l]+=dp[i+1][j][k][l-1];
                            if(len1&&len2&&a[j]==b[k]) dp[i][j][k][l]+=dp[i][j-1][k+1][l];
                        }
                        if(dp[i][j][k][l])
                            ans=max(ans,len1+len2);
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

三 數學考試

題意:
求兩個長度爲k的區間的區間和最大

題解:
前綴和+線性dp

開始還覺得區間和可能會存不下,以爲會暴long long,但是是我算錯了

開始的想法是用滑動窗口來維護區間和,發現還是不能實現,這個dp開始還真沒想到

dp1[i]=max(sum[i]-sum[i-k],dp1[i-1]);  //i之前的最大區間和
dp2[i]=max(sum[i+k-1]-sum[i-1],dp2[i+1]);  //i之後需的最大區間和
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const ll mod=1e9+7;
const int maxn=2e5+5;
ll a[maxn],sum[maxn];
ll dp1[maxn]; //i之前的最大區間和
ll dp2[maxn]; //i之後的最大區間和
int main(){
    int t,n,k;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&k);
        sum[0]=0;
        memset(dp1,-inf,sizeof(dp1));
        memset(dp2,-inf,sizeof(dp2));
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=k;i<=n-k;i++){
            dp1[i]=max(sum[i]-sum[i-k],dp1[i-1]);
        }
        for(int i=n-k+1;i>=k+1;i--){
            dp2[i]=max(sum[i+k-1]-sum[i-1],dp2[i+1]);
        }
        ll ans=-1e18;
        for(int i=k;i<=n-k;i++){
            ans=max(ans,dp1[i]+dp2[i+1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

四 滑動窗口

題意:
一個長度爲k的滑動窗口,求窗口在各個位置的最大值和最小值

題解:
開始打算試試線段樹能不能過,但是很遺憾,mle(內存超限)
於是就用隊列來維護最大/小值

1.用數組模擬隊列

//隊列過長
if(l<=r&&(i-q[l])>=k)
    l++;
//將隊列中比將要入隊列的元素大的通通出隊列
while(l<=r&&a[q[r]]>=a[i]){
    r--;
}

故完整代碼:

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const ll mod=1e9+7;
const int maxn=1e6+5;
int n,k;
int a[maxn],q[maxn];
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    q[1]=1;
    int l=1,r=1;
    int flag=0;
    for(int i=2;i<=n;i++){
        //隊列過長
        if(l<=r&&(i-q[l])>=k)
            l++;
        while(l<=r&&a[q[r]]>=a[i]){
            r--;
        }
        q[++r]=i;
        if(i>=k){
            if(flag==0){
                printf("%d",a[q[l]]);
                flag=1;
            }
            else{
                printf(" %d",a[q[l]]);
            }
        }
    }
    printf("\n");
    l=1,r=1;
    q[1]=1;
    flag=0;
    for(int i=2;i<=n;i++){
        if(l<=r&&(i-q[l])>=k)
            l++;
        while(l<=r&&a[q[r]]<=a[i]){
            r--;
        }
        q[++r]=i;
        if(i>=k){
            if(flag==0){
                printf("%d",a[q[l]]);
                flag=1;
            }
            else{
                printf(" %d",a[q[l]]);
            }
        }
    }
    printf("\n");
    return 0;
}

2.使用STL的deque(支持頭插尾插以及頭刪尾刪):

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const ll mod=1e9+7;
const int maxn=1e6+5;
int a[maxn];
deque<int>q;
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    while(!q.empty()) q.pop_front();
    q.push_back(1);
    int flag=0;
    for(int i=2;i<=n;i++){
        if(!q.empty()&&(i-q.front())>=k){
            q.pop_front();
        }
        while(!q.empty()&&a[q.back()]>=a[i]){
            q.pop_back();
        }
        q.push_back(i);
        if(i>=k){
            if(flag==0){
                flag=1;
                printf("%d",a[q.front()]);
            }
            else{
                printf(" %d",a[q.front()]);
            }
        }
    }
    printf("\n");
    while(!q.empty()) q.pop_front();
    q.push_back(1);
    flag=0;
    for(int i=2;i<=n;i++){
        if(!q.empty()&&(i-q.front())>=k){
            q.pop_front();
        }
        while(!q.empty()&&a[q.back()]<=a[i]){
            q.pop_back();
        }
        q.push_back(i);
        if(i>=k){
            if(flag==0){
                flag=1;
                printf("%d",a[q.front()]);
            }
            else{
                printf(" %d",a[q.front()]);
            }
        }
    }
    printf("\n");
    return 0;
}

五 城市窗口

六 Rinne Loves Edges

題意:
n個頂點,m條邊的無向連通圖,每條邊都有對應的權值
給出一個點s,刪去一些邊,使得不包括這個點,其他度數爲1的點都不能和s聯通

題解:
題目中說m=n-1,這很明顯就是一棵樹,現在問題就轉化爲給定根節點,要使葉子結點不能到達根節點,需要刪除多少條邊,求最少的代價

想到樹形dp

dp[u]+=min(dp[v],c);
//u表示當前節點,v:u的兒子節點,c表示兩節點之間的距離

使用鏈式向前星存圖的代碼:

#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const ll mod=1e9+7;
const int maxn=1e5+5;
int n,m,s;
struct node{
    int to,nxt;
    ll val;
}edge[maxn<<2];
int head[maxn],tot;
ll dp[maxn];
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void add(int u,int v,int c){
    edge[++tot].to=v;
    edge[tot].val=c;
    edge[tot].nxt=head[u];
    head[u]=tot;
}
void dfs(int u,int pre){
    int flag=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==pre) continue;
        flag=1;
        dfs(v,u);
        dp[u]+=min(dp[v],edge[i].val);
    }
    if(flag==0)
        dp[u]=inf;
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    init();
    int u,v,c;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c),add(v,u,c);
    }
    dfs(s,0);
    printf("%lld\n",dp[s]);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章