1. 程式人生 > 實用技巧 >跟光磊學Java應用開發與系統架構-Java程式流程控制

跟光磊學Java應用開發與系統架構-Java程式流程控制

Codeforces Round #683 (Div. 2, by Meet IT)

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;
}