牛客每日一題系列(持續更新)
文章目錄
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;
}