1. 程式人生 > 實用技巧 >牛客團隊賽50&CF#664(Div2)

牛客團隊賽50&CF#664(Div2)

牛客團隊賽50

A.Rental Service

題目:https://ac.nowcoder.com/acm/contest/6306/A

題解:牛有兩種賣法:一個是賣奶,一個是租賃,比較二者哪個獲利最多。

一道典型的貪心問題。

1.留下產奶量多的奶牛;

2.賣牛奶先賣給出價高的商鋪;

3.把奶牛租給出價高的鄰居。

我們按產奶量從多到少排序,按商鋪價格從高到低排序,按出租的價格從高到低排序。

這裡用到一個字首和陣列,來計算奶牛租賃的。

我們列舉第一頭奶牛賣奶,其餘奶牛租賃的最大價格,然後依次列舉,取其最大即可。注意程式碼中的:ans=max(ans,num+c[min(n-i,r)]);c為字首和陣列表示剩餘n-i頭奶牛租賃的最大價格

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll N=100010;
ll n,m,r;
ll a[N],c[N];
ll ans,sum;
struct node
{
    ll q,p;
}b[N];

bool cmp(node a,node b)
{
    if(a.p!=b.p)
        return a.p>b.p;
    else
        return a.q>b.q;
}

bool cmp2(int a,int b) { return a>b; } int main() { ll i,j,k; ll flag=0; cin>>n>>m>>r; for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+n+1,cmp2); for(i=0;i<m;i++) cin>>b[i].q>>b[i].p; sort(b,b+m,cmp);
for(i=1;i<=r;i++) cin>>c[i]; sort(c+1,c+r+1,cmp2); for(i=1;i<=r;i++) c[i]+=c[i-1]; ll num=0; j=0; for(i=1;i<=n;i++) { while(j<m&&a[i]>=b[j].q) { a[i]-=b[j].q; num+=b[j].p*b[j].q; j++; } if(j<m) { b[j].q-=a[i]; num+=a[i]*b[j].p; } //字首和陣列模擬最大值,看有幾個賣奶,幾個租賃 ans=max(ans,num+c[min(n-i,r)]); } cout<<ans<<endl; return 0; }

H:Cow Coupons

題目:https://ac.nowcoder.com/acm/contest/6306/H

解法:也是一道談心題,可從兩方面入手:優惠券足夠,優惠券不夠

①優惠足夠,就看錢數,按優惠後的價格從低到高進行購買

②優惠券不夠,先按照優惠後的價格從低到高進行購買,買完後標記上,直到優惠券不夠了錢還夠,或者優惠券夠錢不夠了。

優惠券夠錢不夠,則直接輸出;優惠券不夠了錢夠,就將陣列按照原來的價格從低到高排序,便利購買時已標記過的不購買,計算直到錢不夠為止。

程式碼:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=50010;
struct node
{
    ll p,c;
    bool flag;
}a[N];

bool cmp1(node a,node b)
{
    if(a.c!=b.c)
        return a.c<b.c;
    else
        return a.p>b.p;
}

bool cmp2(node a,node b)
{
    return a.p<b.p;
}

int main()
{
    ll i,j,n,k,m;
    ll ans=0;
    cin>>n>>k>>m;
    for(i=0;i<n;i++)
    {
        cin>>a[i].p>>a[i].c;
        a[i].flag=false;
    }
    sort(a,a+n,cmp1);
    if(k>=n)
    {
        for(i=0;i<n;i++)
        {
            if(m>=a[i].c)
            {
                m-=a[i].c;
                ans++;
            }
            else
                break;
        }
        cout<<ans<<endl;
    }
    else
    {
        bool flag=false;
        for(i=0;i<n;i++)
        {
            if(m>=a[i].c&&k>0)
            {
                m-=a[i].c;
                a[i].flag=true;
                k--;
                ans++;
            }
            if(k==0)
                break;
            if(m<a[i].c)
            {
                flag=true;
                break;
            }
        }
        if(flag==true)
            cout<<ans<<endl;
        else
        {
            sort(a,a+n,cmp2);
            for(i=0;i<n;i++)
            {
                if(a[i].flag==false)
                {
                    if(m>=a[i].p)
                    {
                        m-=a[i].p;
                        ans++;
                    }
                    else
                        break;
                }
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

J.Haybale Stacking

題目:https://ac.nowcoder.com/acm/contest/6306/J

題解:一道差分題。根據【l,r】,在這個區間每次都加1,因此設計一個差分陣列d[N],他表示d[i]=a[i]-a[i-1],因此,每次給一段範圍,

就讓d[l]++,d[r+1]--。這樣用0(1)的操作解決,之後在遍歷a陣列,a[i]=a[i-1]+d[i];得到最終的a陣列,然後輸出陣列的中間數a[n/2+1]

程式碼:

#include<cstdio>
#include<algorithm>
#define MAXN 1000005
using namespace std;
int a[MAXN],d[MAXN];
int n,p;
int main(){
    scanf("%d%d",&n,&p);
    while(p--){
        int l,r;
        scanf("%d%d",&l,&r);
        d[l]++,d[r+1]--;
    }
    for(int i=1;i<=n;i++){
        a[i]=a[i-1]+d[i];
    }
    sort(a+1,a+n+1);
    printf("%d\n",a[n/2+1]);
    return 0;
}

CF

A. Boboniu Likes to Color Balls

題目:https://codeforces.com/contest/1395/problem/A

題解:要想保證他為迴文串,滿足要求的是:

r%2+g%2+b%2+w%2<=1,意思是奇數個為1個情況

r&&g&&b&&(r%2+g%2+b%2+w%2>=3),奇數個為3個或者4個,並且r,g,b都得大於0,因為需要一次變化才能將其變成迴文串,因此至少他們三個得大於0.

程式碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;

int main()
{
    ll t,i,j,r,g,b,w;
    cin>>t;
    for(i=0;i<t;i++)
    {
        cin>>r>>g>>b>>w;
        if(r%2+g%2+b%2+w%2<=1||(r&&g&&b&&(r%2+g%2+b%2+w%2>=3)))
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

B. Boboniu Plays Chess

題目:https://codeforces.com/contest/1395/problem/B

題解:從初始位置開始,確定一條唯一的遍歷路線即可。

我的路線是:先從他的位置開始遍歷其右邊的位置,一直到邊緣,然後再開始從他的左邊開始遍歷到左邊緣,然後向上遍歷,在向下遍歷。很簡單的

程式碼:

#include<iostream>
using namespace std;
const int N=110;
int sx,sy;

int main()
{
    int i,j,n,m,k;
    cin>>n>>m>>sx>>sy;
    for(i=sy;i<=m;i++)
        cout<<sx<<" "<<i<<endl;
    for(i=sy-1;i>=1;i--)
        cout<<sx<<" "<<i<<endl;
    k=sx-1;
    int flag=0;
    while(k>=1)
    {
        if(flag==0)
        {
            for(i=1;i<=m;i++)
                cout<<k<<" "<<i<<endl;
            flag=1;
            k--;
        }
        else
        {
            for(i=m;i>=1;i--)
                cout<<k<<" "<<i<<endl;
            flag=0;
            k--;
        }
    }
    k=sx+1;
    while(k<=n)
    {
        if(flag==0)
        {
            for(i=1;i<=m;i++)
                cout<<k<<" "<<i<<endl;
            flag=1;
            k++;
        }
        else
        {
            for(i=m;i>=1;i--)
                cout<<k<<" "<<i<<endl;
            flag=0;
            k++;
        }
    }
    return 0;
}

C. Boboniu and Bit Operations

題目:https://codeforces.com/contest/1395/problem/C

題解:一開始想的是用map陣列來儘可能的收集相同的比a[i]本身要小的數,結果第三個樣例都沒過,然後知道這樣沒依據,行不通

然後看K的資料範圍試2^9,n和m是200,列舉k,複雜度為O(knm),最大是2*10^8次方,還是可以接受的

因此列舉k,然後判斷整個k能否於a[i]&b[j]進行或運算,並且等於k,如果可以遍歷整個a陣列,那麼k就是最終的答案。

程式碼:

#include <cmath>
#include <cstdio>
#include <cstring>
#include<iostream>
using namespace std;
int n, m, a[205], b[205];
int main() {
    int i,j,k;
    cin>>n>>m;
    for(i=0;i<n;i++)
        cin>>a[i];
    for(i=0;i<m;i++)
        cin>>b[i];
    for(k=0;k<=(1<<9);k++)
    {
        bool f=false;
        for(i=0;i<n;i++)
        {
            bool flag=false;
            for(j=0;j<m;j++)
            {
                if((k|(a[i]&b[j]))==k)
                {
                    flag=true;
                    break;
                }
            }
            if(flag==false)
            {
                f=true;
                break;
            }
        }
        if(f==false)
        {
            cout<<k<<endl;
            break;
        }
    }
    return 0;
}

D. Boboniu Chats with Du

題目:https://codeforces.com/contest/1395/problem/D

題解:對於這個,我們採取貪心的思想,將大於的分為一組,小於的分為一組,然後按從大到小排序。

構造字首和陣列,然後我們的思想是,大於的那一組出現一次就要佔去d+1個天數,實則不是,可以將其放到最後,那麼就會只站一天,因此我們要充分的利用最後一天。

因此只要列舉從0~len1天,a中可以出現的個數,剩餘的就是b中個數,然後取最大。

務必要注意一點:判斷k>n,否則會導致陣列下標不在範圍內。

程式碼:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll n,m,d;
ll a[N],b[N];
ll pra[N],prb[N];
bool cmp(ll a,ll b)
{
    return a>b;
}
ll ans;
int main()
{
    ll i,j,x;
    ll len1=0,len2=0;
    cin>>n>>d>>m;
    for(i=1;i<=n;i++)
    {
        cin>>x;
        if(x>m)
            a[++len1]=x;
        else
            b[++len2]=x;
    }
    sort(a+1,a+len1+1,cmp);
    sort(b+1,b+len2+1,cmp);
    for(i=1;i<=len1;i++)
        pra[i]=pra[i-1]+a[i];
    for(i=1;i<=len2;i++)
        prb[i]=prb[i-1]+b[i];
    ans=prb[len2];
    for(i=1;i<=len1;i++)
    {
        int k=(i-1)*(d+1)+1;
        //不判斷會導致陣列超界
        if(k>n)
            break;
        ll t=min(n-k,len2);
        ans=max(ans,pra[i]+prb[t]);
    }
    cout<<ans<<endl;
    return 0;
}