ACM日常訓練日記——7.25

冬天的睡袋發表於2024-07-25
  • Atcoder訓練
    1. Harlequin
      思維題博弈論,思考每一次怎麼轉化最優,存在兩個答案說明f可以贏,打表發現當所有數字都是偶數時,答案為second,否則為first
#include <bits/stdc++.h>

using namespace std;

using ll=long long;

int main(){
	
	ll n;
	cin>>n;
	ll ans=0;
	vector<ll>v(n+1);
	for(int i=1;i<=n;i++){
		ll b;
		cin>>b;
		if(b%2==0)ans++;
	}
	if(ans==n)cout<<"second";
	else
		cout<<"first";
}
  1. Gathering Children
    思維,模擬打表,最終會聚集在LR兩個點上,我們考慮最終要不要交換兩個點,打表模擬情況
    最後會發現如果:R+L為偶數,不變,如果R為奇數,那麼L++,R不變,如果L為奇數,R++,L不變
#include <bits/stdc++.h>

using namespace std;

using ll=long long;
ll res[100001];

int main(){
	string s;
	cin>>s;
	int	n=s.length();
	vector<ll>v(n+1);
	int le=1;
	for(int i=0;i<n;i++){
		if(s[i]=='R'&&s[i+1]!='R'){
			v[i]+=le,le=1;
		}else if(s[i]=='R'&&s[i+1]=='R')le++;
		else le=1;
	}
	le=1;
	for(int i=n-1;i>=0;i--){
		if(s[i]=='L'&&s[i-1]!='L'){
			v[i]+=le,le=1;
		}else if(s[i]=='L'&&s[i-1]=='L')le++;
		else le=1;
	}
	for(int i=0;i<n;i++)
	{
		if(v[i]!=0)
		{
			if((v[i]+v[i+1])%2==0)
			{
				res[i]=(v[i]+v[i+1])/2;
				res[i+1]=res[i];
			}
			else
			{
				res[i]=floor((v[i]+v[i+1])/2);
				res[i+1]=res[i];
				if(v[i]%2==0)	res[i+1]++;
				else res[i]++;
			}
			i++;
		}
	}
	for(int i=0;i<n;i++)	cout<<res[i]<<" ";
}
  1. Palindrome Hard Problem
    之前沒做出來的題,不難但是我做不出來,每次分出來一個數就是一個迴文串,我們統計多少個數就是答案,我把每行可能m個看出成都是一個了
#include <bits/stdc++.h>
using namespace std;

using ll =long long;
map<char,ll>mp;
int main(){
	ll n;
	int sum=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		int k=s.length();
		sum+=k;
	}
	cout<<sum;
}
  1. Storybooks
    標準字首和+二分,沒做過可以回去再做一下,這道題注意邊界和資料範圍就行
#include <bits/stdc++.h>
using namespace std;

using ll =long long;

ll prefix[10011000];
int main(){
	int n,k;
	cin>>n>>k;
	vector<ll>v(n+1);
	vector<ll>d(k+1);
	for(int i=1;i<=n;i++)cin>>v[i];
	
	for(int i=1;i<=k;i++)cin>>d[i];
	
	sort(v.begin()+1,v.end());
	for(int i=1;i<=n;i++)prefix[i]+=v[i]+prefix[i-1];
	for(int i=1;i<=k;i++){
		ll l=0,r=n;
	while(l<=r){
		ll mid=(l+r)/2;
		if(prefix[mid]>d[i]){
			r=mid-1;
		}else
			l=mid+1;
	}
		cout<<r<<' ';
	}
}
  • 動態規劃專項訓練
    1.P1434 [SHOI2002] 滑雪
    利用記憶化dfs搜素的方法找到最長距離,好題,學會去用記憶化搜素。
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int MAXN = 105;
ll v[MAXN][MAXN];
ll dp[MAXN][MAXN];
int n, m;

int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};

ll dfs(int x, int y) {
    //這裡就是記憶化
	if (dp[x][y] != 0) return dp[x][y];
	dp[x][y] = 1;
	for (int i = 0; i < 4; ++i) {
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && v[nx][ny] < v[x][y]) {
			dp[x][y] = max(dp[x][y], dfs(nx, ny) + 1);
		}
	}
	return dp[x][y];
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			cin >> v[i][j];
		}
	}
	
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			ans = max(ans, dfs(i, j));
		}
	}
	
	cout << ans << endl;
	return 0;
}

2.最長公共子序列
二維dp,狀態轉移方程a[i]=b[i]時,dp[i][j]=dp[i-1][j-1]+1,否則dp[i][j]=max(dp[i-1][j],dp[i][j-1])時間複雜度為n*n太高
最長公共子序列有專門的問題集最長公共子序列LCS問題
dp程式碼,注意邊界dp[0][0]=0;

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

using ll = long long;
const int MAXN = 5001;
ll a[1000010];
ll b[100010];
ll dp[MAXN][MAXN];
int n, m;

int main(){
	string s;
	string t;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];

	ll ans=1;
	dp[0][0]=0;

	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[i]==b[j]){
				dp[i][j]=dp[i-1][j-1]+1;
			}else{
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
			ans=max(ans,dp[i][j]);
			
		}
	}
	cout<<ans;
}
對於排列的 LCS 問題,有一種特殊的方法可以線上性時間內解決 我們可以利用排列的性質,將問題轉化為最長遞增子序列(LIS)問題,而 LIS 問題可以在 O(n log n) 時間內解決。
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int MAXN = 100010;
int a[MAXN];
int b[MAXN];
int pos[MAXN];
int dp[MAXN];
int n;

int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];

    // 將 a 中的元素位置對映到 b 中
    for(int i = 1; i <= n; i++) {
        pos[b[i]] = i;
    }

    // 將 a 中的元素替換為在 b 中的位置,然後求 LIS
    for(int i = 1; i <= n; i++) {
        a[i] = pos[a[i]];
    }
    // 求 LIS
    int len = 0;
    for(int i = 1; i <= n; i++) {
        int idx = lower_bound(dp, dp + len, a[i]) - dp;
        dp[idx] = a[i];
        if(idx == len) len++;
    }
    cout << len << endl;
    return 0;
}

揹包問題
3.NOIP2001 裝箱問題
01簡化揹包的基本過程,怎麼去選取,還有最佳化記憶體大小的01滾動,就地滾動,轉移方程dp[i%2][j]=dp[(i-1)%2][j]||dp[(i-1)%2][j-a[i]]

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

using ll = long long;
const int MAXN = 100010;
int pos[MAXN];
int dp[2][20020];
int a[40];
int n,m;
// 01揹包  01滾動
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int v,n;
	cin>>v>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	dp[0][0]=1;
	/*
	  01滾動
	for(int i=1;i<=n;i++){
		for(int j=0;j<=v;j++){
	  //解釋:還有空位放的話會放自己的值加上前面選的值加上當前的值,否則只放當前的值
	  //注意j不能從a[i]開始而是j從0開始,原因是小於a[i]的數也要留到下一輪
			if(j>=a[i]){
				dp[i%2][j]=dp[(i-1)%2][j]||dp[(i-1)%2][j-a[i]];
			}else
				dp[i%2][j]=dp[(i-1)%2][j];
		}
	}
	  */
    //就地滾動
	for(int i=1;i<=n;i++){
		for(int j=v;j>=a[i];j--)
		  dp[j]=dp[j]||dp[j-a[i]];
	}
	ll ans=0;
	for(int i=v;i>=0;i--){
		if(dp[i]==1){
			ans=i;
			break;
		}
	}
	cout<<ans;
	
}

2.【模板】01揹包
現在目前有兩種問法,一種是揹包能裝的最大價值,另一種是剛好裝滿的情況下,揹包的最大價值
第一張解決方法的轉移方程和第二種是一樣的dp[j]=max(dp[j].dp[j-a[i]]+w[i])算出來不一定剛剛好容量大價值最大,但是一定在裡面;
第二種是初始化為負無窮,最終判斷pd[v]有沒有初始化,有的話剛剛好滿足條件的答案就是pd[v],都用01滾動

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010;

int f[N]; // f[j]表示體積為j的情況下的總價值
int v[N], w[N]; // 物品的體積 價值

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
    memset(f, -0x3f, sizeof f);  //一開始都初始化為負無窮  方便記錄是否有恰好體積為j的情況出現過  
    f[0] = 0; // 最開始體積為0價值為0
    for(int i = 1; i <= n; i ++) 
        for(int j = m; j >= v[i]; j --) // j>=v[i] 保證了可以選擇第i個物品
        {
            f[j] = max(f[j], f[j - v[i]] + w[i]); // 這裡其實消去了一維
           // 原式為f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
           // 為了防止計算時所需要的上一層的數值被覆蓋所以倒序遍歷這樣算f[j]時用到的f[j - v[i]]就還是和原來一樣
        }
    
    int ans1 = 0, ans2 = 0;
    for(int i = 0; i <= m; i ++) ans1 = max(ans1, f[i]); // 找到最大值
    
    if(f[m] < 0) ans2 = 0; // 如果f[m]<0說明沒被初始化過 沒有體積恰好為m的情況出現
    else ans2 = f[m];  //否則根據定義可知 f[m]的值就是揹包恰好裝滿的情況下的最大值
    
    cout << ans1 << endl;
    cout << ans2 << endl;
}

  • 牛客萌新聯賽(第一場)(覆盤)
    1.造數
    逆向思維,怎麼把n變成0,能除2,就除,不能就減
#include<bits/stdc++.h>

using namespace std;

using ll =long long;

ll n,m,k,t;
ll v[1000100];
bool is(ll n) 
{
	return n > 0 && (n & -n) == n; 
}
int main(){
	cin>>n;
	if(n==0)cout<<0;
	else if(n==1)cout<<1;
	else if(n==2)cout<<1;
	else if(n==3)cout<<2;
	else if(n==4)cout<<2;
	else{
		ll ans=1;
		
		if(is(n)){
			while(n>2){
				n/=2;
				ans++;
			}
		}else{
			ll ans = 0;
			while (n >= 2) {
				if (n % 2 == 0) {
					n /= 2;
				} else {
					n -= 1;
				}
				ans++;
			}
            cout<<ans;
		}
			
	}
	return 0;
}

2.愛探險的朵拉
很好的記憶化搜素,我們需要認認真真學,這個每個路線判斷是否已經走過,或者怎麼避免已經走過的路線

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
vector<int> v(100010);
vector<int> b(100010);

int ma=0;
int cnt=1;
void bfs(int x,int l){
        //每次標記走過的
		b[x]=cnt;
        //直接找最大
	    ma=max(ma,l);
        //判斷是否走過,這裡不需要擔心,不會走重複,之前走過的已經標記
	if(b[v[x]]!=cnt){
		bfs(v[x],l+1);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>v[i];
	for(int i=1;i<=n;i++){
        //這裡就是原因
		if(b[i]==0){
          //cnt不同,路線不同
			cnt++;
			bfs(i,1);
		}
	}
	cout<<ma;
}

3.旅途的終點
思維+二分,二分答案x一定能走到的國家數,是按順序走的,然後每次二分檢查的時候排序前x個國家數,前面的數我們不用神力,如果滿足條件就l=mid+1.
這種按順序的二分和思維一定要掌握

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

using ll =long long;
vector<ll>v(2e5+8);
ll n,m,k;
bool check(ll x){
	ll hp=m;
	vector<ll>s(n+1);
    s=v;
	sort(s.begin()+1,s.begin()+1+x,greater<ll>());
	for(int i=k+1;i<=x;i++){
		if(hp<=s[i])return false;
		  hp-=s[i];
	}
	return true;
}

int main(){
	cin>>n>>m>>k;
    ll ans=0;
	for(int i=1;i<=n;i++)cin>>v[i];
	ll l=0,r=n;
	while(l<=r){
		ll mid=(l+r)/2;
		if(check(mid)){
			l=mid+1; 
            ans=mid;
		}else
			r=mid-1;
	}
	cout<<ans;
}

相關文章