1. 程式人生 > 其它 >牛客歡樂賽1

牛客歡樂賽1

牛客歡樂賽1

比賽地址

A

​ 自己最開始想著這什麼鬼,把石子在數軸上排序,然後列舉距離看是否滿足條件,再一看距離好傢伙任意實數,屬實不能做。就放棄了。

​ 正確的做法倒是也很簡單,我們舉一個例子來說的話

在這裡插入圖片描述

​ 我們不用考慮具體的中心位置設定在哪裡,對於上圖而言我只需要把中心位置設定的離第一個紅色球更近就可以滿足條件,求出來最大是4個。所以問題就轉化成了,需要考慮在兩個相鄰的藍球中的最多紅球的個數。但是需要注意左邊沒有藍球以及右邊沒有藍球的兩種特殊情況。

​ 得到上述的結論之後就是來計算了,最開始自己寫的是藍紅分為兩個陣列,然後進行遍歷。最後超時。這裡採用分組過後可以採用STL中的lower_bound與upper_bound

lower_bound

lower_bound(起始地址,結束地址,要查詢的數值) 返回的是數值 第一個 出現的位置。

upper_bound

upper_bound(起始地址,結束地址,要查詢的數值) 返回的是 第一個大於待查詢數值 出現的位置。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> red;
vector<int> blue;

int t,n,m;
bool cmp(int a,int b){
    return a < b;
}

void work(){
    sort(red.begin(),red.end(),cmp);
    sort(blue.begin(),blue.end(),cmp);
    int cnt = 0;
    int ans = -1;
    for(int i = 0;i < blue.size() - 1;i++){
        int l = blue[i];
        int r = blue[i + 1];
        cnt = lower_bound(red.begin(),red.end(),r) - lower_bound(red.begin(),red.end(),l + 1);
        ans = max(ans,cnt);
    }
    if(ans == 0){
        printf("Impossible\n");
    }else{
        printf("%d\n",ans);
    }
}

void input(){
    scanf("%d",&t);
    int s;
    while(t--){
        scanf("%d%d",&n,&m);
        red.clear();
        blue.clear();
        for(int i = 0;i < n;i++){
            scanf("%d",&s);
            red.push_back(s);
        }
        for(int i = 0;i < m;i++){
            scanf("%d",&s);
            blue.push_back(s);
        }
      	//處理兩種特殊情況
        blue.push_back(0);
        blue.push_back(1e9+10);
        work();
    }
}


int main(){
    input();
    return 0;
}


除了兩個陣列排序之外,還可以使用map的方法解決

#include <cstdio>
#include <map>
using namespace std;
map<int,int> pos,cnt;
int t,n,m;


void work(){
    int max = -1;
    int ans = 0;
    for(auto &it:pos){
        if(it.second == 1){
            ans += cnt[it.first];
        }else{
            max = max > ans ? max : ans;
            ans = 0;
        }
    }
    max = max > ans ? max : ans;
    if(max == 0){
        printf("Impossible\n");
    }else{
        printf("%d\n",max);
    }
}

void input(){
    scanf("%d",&t);
    while(t--){
        pos.clear();
        cnt.clear();
        scanf("%d%d",&n,&m);
        for(int i = 0;i < n;i++){
            int s;
            scanf("%d",&s);
            pos[s] = 1;
            cnt[s]++;
        }
        for(int i = 0;i < m;i++){
            int s;
            scanf("%d",&s);
            pos[s] = 0;
            cnt[s] = 0;
        }
        work();
    }


}

int main(){
    input();
    return 0;
}

B

​ 對於這道題來說,我們可以很方便地根據上一行的資料來推斷出這一行的狀態,例如

在這裡插入圖片描述

比如這裡我們假設第一列第一行有雷,那麼根據第二列的第一行我們可以知道第二行是沒有雷的,根據第二行我們可以得到第三行是有雷的,那麼第四行就是沒雷的。那麼我們還可以假設第一行是沒有雷的。這樣的流程過後,我們會發現,只要第一行的狀態是確定的,那麼其餘行的狀態是可以推出的,也就是確定的。所以答案要麼是1要麼是2(第一行沒雷或者有雷)。假定完之後,判斷一下是否衝突即可。

#include <cstdio>
#include <algorithm>
using namespace std;

int n;
int a[10010];
int l[10010];

int cal(){
	for(int i = 0;i < n - 1;i++){
		int left;
		if(i == 0){
			left = a[i] - l[i];
		}else{
			left = a[i] - l[i] - l[i - 1];
		}
		if(left == 1){
			l[i + 1] = 1;
		}else if(left == 0){
			l[i + 1] = 0;	
		}else{
			return 0;
		}
	}
	if(a[n - 1] == l[n - 1] + l[n - 2]){
		return 1;
	}else{
		return 0;
	}
}

void work(){
	int ans = 0;
	l[0] = 1;
	if(cal()) ans++;
	l[0] = 0;
	if(cal()) ans++;
	printf("%d",ans);
}

void input(){
	scanf("%d",&n);
	for(int i = 0;i < n;i++){
		scanf("%d",&a[i]);
	}
}

int main(){
	input();
	work():
	return 0;
}

C

​ 在這裡我們首先考慮沒有施法的情況,對於每一個糖糖,只要有跟他不同組的糖糖在他的後面,且能力值大於它,它就會死。那麼我們只需要從後往前遍歷,同時維護兩個組的最大值。每次遇到一個糖糖的時候就用它和另一組的最大值進行比較,只要小於,糖糖就會死。

​ 我們再考慮施法的情況,在施法的時候,其實是對前面的序列造不成影響的,因為這部分的大小關係是不會改變的,改變的只有後半部分之間的關係。而我們每一次是隻判斷前半部分的關係。所以這裡逐步施法跟放在一起施法是沒有影響的。

​ 每次施法的處理,這裡如果使用for迴圈的話就會導致超時,這裡就用到了差分陣列,記錄下相鄰兩個數字的差值,每一次施法的時候,施法處的差值-1,由於每一次都是從1到x加1,所以這裡就使最開頭的差值++就可以了

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

typedef struct{
    int no;
    int ability;
}child;

vector<child> temp;
int delta[50007];


int t,n,m,ans;

void change(int time){
    for(int i = 0;i < time;i++){
        temp[i].ability++;
    }
}

void work(){
    int t[2] = {-1,-1};
    for(int i = n - 1;i >= 0 ;i--){
        int group = temp[i].no;
        if(t[1 - group] > temp[i].ability){
            ans--;
        }
        t[group] = max(t[group],temp[i].ability);
    }
    printf("%d\n",ans);
}

void input(){
    scanf("%d",&t);
    while(t--){
        temp.clear();
        scanf("%d%d",&n,&m);
        ans = n;
        for(int i = 0;i < n;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            child s;
            s.no = a;
            s.ability = b;
            temp.push_back(s);
            if(i > 0){
                delta[i] = temp[i].ability - temp[i - 1].ability;
            }
        }
        for(int i = 0;i < m;i++){
            int time;
            scanf("%d",&time);
            delta[0]++;
            delta[time]--;
        }
        for(int i = 0;i < n;i++){
            if(i == 0){
                temp[i].ability = delta[i] + temp[i].ability;
            }else{
                temp[i].ability = temp[i - 1].ability + delta[i];
            }
        }
        work();
    }
}


int main(){
    input();
    return 0;
}

D

​ 最開始看到還以為是搜尋題,直接上手深度搜索,最後超時了。正解為動態規劃題目。每次走到一個格子之後的狀態與該格子之前的狀態無關,僅與之後的狀態相關,所以考慮用動態規劃。這裡走到的位置我們可以用$ i1+j2+k3+l4$來表示

d p [ i ] [ j ] [ k ] [ l ] = m a x ( d p [ i − 1 ] [ j ] [ k ] [ l ] , d p [ i ] [ j − 1 ] [ k ] [ l ] , d p [ i ] [ j ] [ k − 1 ] [ l ] , d p [ i ] [ j ] [ k ] [ l − 1 ] ) + a [ i ∗ 1 + j ∗ 2 + k ∗ 3 + l ∗ 4 ] dp[i][j][k][l] = max(dp[i-1][j][k][l],dp[i][j-1][k][l],dp[i][j][k-1][l],dp[i][j][k][l-1])+a[i*1+j*2+k*3+l*4] dp[i][j][k][l]=max(dp[i1][j][k][l],dp[i][j1][k][l],dp[i][j][k1][l],dp[i][j][k][l1])+a[i1+j2+k3+l4]

i,j,k,l分別表示的就是1,2,3,4四張卡片的數量,

#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;

int a[357];
int dp[43][43][43][43];
int cnt[5];
int n,m;

int dfs(int i,int j,int k,int l){
    if(i < 0 || j < 0 || k < 0 || l < 0) return 0;
    if(dp[i][j][k][l] != 0) return dp[i][j][k][l];
    dp[i][j][k][l] = max(max(dfs(i - 1,j,k,l),dfs(i,j- 1,k,l)),
                         max(dfs(i,j,k - 1,l),dfs(i,j,k,l - 1)))
                    +a[i * 1 + j * 2 + k * 3 + l * 4];
    return dp[i][j][k][l];
}

void work(){
    printf("%d\n",dfs(cnt[1],cnt[2],cnt[3],cnt[4]));
}

void input(){
    scanf("%d%d",&n,&m);
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]);
    }
    memset(cnt,0,sizeof(cnt));
    for(int i = 0;i < m;i++){
        int temp;
        scanf("%d",&temp);
        cnt[temp]++;
    }
}

int main(){
    input();
    work();
    return 0;
}

E

我們在這裡假定給定的區間為[l,r],假設我們知道從l開始經過s個區間後的位置t,如果r大於t,那麼我們就還需要向後找。當然這裡不可能一個一個地找過去。在這裡我們可以用二進位制的表示。我們假設想找的範圍在第一個數的第7個區間裡。我們可以先到第一個數的第四個區間。從第四區間到第六區間,從第六區間到第七區間。即7 = 4 + 2 + 1。所以這裡我們需要求的就是每一l第0,1,2,4。。。區間右邊的位置。

#include <cstdio>
#include <algorithm>
using namespace std;


int n,m,k;
int a[1000100];
int f[1000100][23];

void input(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
    }
}

void prework(){
    long long sum = 0;
    for(int j = 0;j <= 20;j++){
        for(int i = 1;i <= n + 1;i++){
            f[i][j] = n + 1;
        }
    }
    //求第0個區間
    for(int i = 1,j = 0;i <= n;i++){
        while(sum <= k && j <= n){
            ++j;
            sum += a[j];
        }
        f[i][0] = j;
        sum -= a[i];
    }
    //8 = 4 + 4
    //也就是為了求第i個區間轉變成求j的i-1區間
    //和從i-1區間右邊開始的i-1區間的和
    for(int i = 1;i <= 20;i++){
        for(int j = 1;j <= n;j++){
            f[j][i] = f[f[j][i-1]][i-1];
        }
    }
}

void calc(int l,int r){
    long long ans = 0;
    for(int i = 20;i >= 0;i--){
        if(r >= f[l][i]){
            ans += 1 << i;
            l = f[l][i];
        }
    }
    if(f[l][0] <= r) ans = -1;
    if(ans == -1){
        printf("Chtholly\n");
    }else{
        printf("%lld\n",ans + 1);
    }
}

void work(){
    for(int i = 0;i < m;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        calc(l,r);
    }
}


int main(){
    input();
    prework();
    work();
    return 0;
}