跟光磊學Java應用開發與系統架構-Java程式流程控制
A,B過水。
C:給20w個數,給你一個數K,問你能不能在這20w個數裡,找到若干個數使得它們的和超過ceil[K/2],且不超過K。
題解:如果20w個數中,有一個滿足的,那直接白給。否則,在小於ceil[k/2]的數中,你直接往上加,如果滿足,就輸出。加不夠則不滿足。你可以一個一個加的原因就在於,兩個小於ceil(K/2)的數加起來是不會超過K的。我寫的時候,加了個排序,屬實沒必要。
#include <bits/stdc++.h> using namespace std; const int N = 200005; #define fi first #define se second typedef pair<int,int> PII; typedef long long LL; int n; LL w; PII a[N]; int main(){ int _;cin>>_; while(_--){ cin>>n>>w; LL s=0; for(int i=1;i<=n;++i){ int x;cin>>x; s+=x; a[i]=make_pair(x,i); } sort(a+1,a+n+1); if(s<(1+w)/2){ cout<<"-1"<<endl; continue; }else{ int idx=n; while(idx>0&&a[idx].fi>w)--idx; vector<int> v; s=0; for(int i=idx;i>=1;--i){ s+=a[i].fi; v.push_back(a[i].se); if(s>=(w+1)/2)break; } if(s<(w+1)/2){ cout<<"-1"<<endl; continue; } cout<<v.size()<<endl; for(auto& x:v){ cout<<x<<" "; } cout<<endl; } } return 0; }
D:給你兩個字串,你可以選取這兩個字串的字串。並且計算它們的分數,分數的定義是,最長公共子序列*4-兩個子串的長。
題解:定義\(dp[i][j]\)為,以i結尾的a串,以j結尾的b串所對應的最大分數。其實它的轉移是比較顯然的。a[i]==b[j]時,\(dp[i][j]=2+max(dp[i-1][j-1],0)\)。否則,\(dp[i][j]=max(-2,dp[i-1][j-1]-2)\)。當然,對於所有情況\(dp[i][j]=max(dp[i-1][j],dp[i][j-1])-1\)的轉移都是成立的。
#include<bits/stdc++.h> using namespace std; const int N = 5005; char a[N], b[N]; int dp[N][N]; int n, m; void chmax(int& x,int y){ if(y>x)x=y; } int main(){ cin>>n>>m; cin>>a+1>>b+1; int ans=0; memset(dp,-0x3f,sizeof dp); dp[0][0]=0; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ if(a[i]==b[j]){ chmax(dp[i][j],dp[i-1][j-1]+2); chmax(dp[i][j],2); }else{ chmax(dp[i][j],dp[i-1][j-1]-2); chmax(dp[i][j],-2); } chmax(dp[i][j],dp[i-1][j]-1); chmax(dp[i][j],dp[i][j-1]-1); } } for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ chmax(ans,dp[i][j]); } } cout<<ans; return 0; }
E: 給你n個不同的數,每一個數會與和自己xor值最小的數連邊,重邊算一條。問你最少刪除多少個數字,使得它們形成的無向圖是一棵樹。
題解:建立01trie,從高位向低位考慮,如果某一個節點的兩顆子樹size都大於2的話,那顯然是不行的。因為它們肯定會選擇自己子樹中的數字組成最小的xor值。再考慮到,由於n個數中,存在一個客觀上的最小xor值,所以必有兩個數互相連。這說明我們的圖,最多隻有n-1條邊。此時想成為一棵樹,只需要這個無向圖是聯通的就可以了。如果我們讓01trie任意一層的子樹,不存在兩個子樹size都大於2。那麼就可行了。因為每次剩下最多一個(選擇其中一個子樹,使其size為1),會連向其他的連通塊,這樣會構成一種合法的方案。
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
int n;
int ans;
int trie[N<<5][2], cntNode, cnt[N<<5];
void Insert(int x){
int cur=0;
for(int i=30;i>=0;--i){
if(!trie[cur][x>>i&1]){
trie[cur][x>>i&1]=++cntNode;
}
cur=trie[cur][x>>i&1];
++cnt[cur];
}
}
void dfs2(int nd, int dep, int cst){
if(dep==30){
ans=min(ans,cst);
return;
}
if(!trie[nd][0]){
dfs2(trie[nd][1],dep+1,cst);
}else if(!trie[nd][1]){
dfs2(trie[nd][0],dep+1,cst);
}else{
dfs2(trie[nd][0],dep+1,cst+cnt[trie[nd][1]]-1);
dfs2(trie[nd][1],dep+1,cst+cnt[trie[nd][0]]-1);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
int x;scanf("%d",&x);
Insert(x);
}
ans=n;
dfs2(0,0,0);
printf("%d",ans);
return 0;
}
F: 給你n個數,你需要找到一個最長的區間,滿足的條件是裡面的眾數不止一個。
題解:首先有一個觀察,對於這樣的一個最大區間,區間眾數肯定是在其中的(如果整個區間的區間眾數不止一個,那直接白給),這個比較顯然,考慮答案區間有一個替換更優的性質。對於easy version,這個時候已經可以開始列舉是哪個數了,因為不同的數最多100個。但是這個方法不足以解決hard version。
考慮出現次數比較多的數字,比如出現次數大於sqrt(n)的數字。可以直接考慮列舉這個數,可以發現這樣的數不超過sqrt(n)個。
對於出現次數比較少的數字,比如出現次數小於sqrt(n)的數字。由於它要成為區間眾數,所以出現次數也不會特別多,上限是sqrt(n)次。所以我們直接枚舉出現次數,尺取答案就好。
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, mx, big, a[N], cnt[N], lst[N<<1], processed[N];
int solve1(int num){
memset(lst,-1,sizeof lst);
int res=0;
int now=0;
lst[now+n]=0;
for(int i=1;i<=n;++i){
if(a[i]==big)++now;
if(a[i]==num)--now;
if(lst[now+n]!=-1){
res=max(res,i-lst[now+n]);
}
if(lst[n+now]==-1){
lst[n+now]=i;
}
}
return res;
}
int solve2(int limit){
int res=0;
int eql=0;
memset(cnt,0,sizeof cnt);
for(int i=1,j=1;i<=n;++i){
++cnt[a[i]];
if(cnt[a[i]]==limit)++eql;
while(j<=i&&cnt[a[i]]>limit){
--cnt[a[j]];
if(cnt[a[j]]==limit-1)--eql;
++j;
}
if(eql>=2){
res=max(res,i-j+1);
}
}
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
++cnt[a[i]];
if(cnt[a[i]]>mx){
mx=cnt[a[i]];
big=a[i];
}
}
int ans=0;
int up=sqrt(n);
for(int i=1;i<=n;++i){
if(cnt[a[i]]>=up&&a[i]!=big&&!processed[a[i]]){
processed[a[i]]=1;
ans=max(ans,solve1(a[i]));
}
}
for(int i=1;i<up;++i)ans=max(ans,solve2(i));
cout<<ans;
return 0;
}