2019牛客第二场补题(F/H)

17 篇文章 0 订阅
11 篇文章 0 订阅

F.

把2*n个人分成 两个人数相等的队伍,每两个人之间都有一个竞争值,当分成两个队伍后,两个队伍之间的竞争值之和最大为多少

做法: 对每个人进行搜索将它放在a或b队里,每放一个人就计算一下当前的竞争值,这样往后搜索的时候就可以利用一下前面的竞争值,避免重复计算,(还有一个小优化,可以把第一个人固定放在第一个队伍里,可以减少一半的状态空间)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll c[30][30];
vector<int>a;
vector<int>b;
ll ans=0;
void dfs(ll now, ll pa, ll pb, ll sum)
{
    if(now==n*2+1)
    {
        ans=max(ans,sum);
        return ;
    }
    if(pa<n)
    {
        ll add=0;
        for(int i=0;i<b.size();i++)
        {
            add+=c[now][b[i]];
        }
        a.push_back(now);
        dfs(now+1,pa+1,pb,sum+add);
        a.pop_back();
    }
    if(pb<n)
    {
        ll add=0;
        for(int i=0;i<a.size();i++)
        {
            add+=c[now][a[i]];
        }
        b.push_back(now);
        dfs(now+1,pa,pb+1,sum+add);
        b.pop_back();
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n*2;i++)
    {
        for(int j=1;j<=n*2;j++)
        {
            scanf("%lld",&c[i][j]);
        }
    }
    a.push_back(1);
    dfs(2,1,0,0);
    cout<<ans<<endl;
    return 0;
}

H.

题意:给你一个N*M的01矩阵,求第二大的全为1的长方形的面积

题解:把每一行当做长方形的底边,O(N*M)预处理出每个以1为宽,高为h的全1 长方形,然后对每个底边使用单调栈求出最大的全1长方形的面积(x*y) 再把(x-1)*y 和x*(y-1) 和x*y 放入一个只维护最大两个元素的multiset,最后的答案就是multiset里第二大的元素。

几个注意点:

1.预处理计算高度的时,只有连续的1才能被计算进去,不连续的1以0处理。

2.初始指针p指向0,高度s[p]=-1(目的不让指针越界,如果你的入栈条件是当前指针指向的元素<要入栈的元素, 最好条件改成当前指针的元素<=要入栈的元素,可以避免这个问题)

3.每条底边最后再额外增加一个高度为0宽度为1的长方形,(使栈中的全部长方形能够出栈更新)

4.ans的更新条件为 累计宽度we*当前高度s[p]>=ans,此时需要更新multiset,防止一条底边中有多个面积相同的长方形而只计算了一次。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int mp[1003][1003];
char sp[1003][1003];
int h[1003][1003];
multiset<int>q;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",sp[i]+1);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            mp[i][j]=sp[i][j]-'0';
            if(h[i-1][j])
            {
                if(mp[i][j])h[i][j]=h[i-1][j]+mp[i][j];
                else h[i][j]=0;
            }
            else h[i][j]=mp[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        int ans=-1;
        int p=0;
        int s[1003];
        int w[1003];
        int x,y;
        s[p]=-1;
        w[p]=0;
        for(int j=1;j<=m+1;j++)
        {
            if(s[p]<h[i][j])
            {
                s[++p]=h[i][j],w[p]=1;
            }
            else
            {
                int we=0;
                while(p!=-1)
                {
                    if(s[p]<h[i][j])
                    {
                        s[++p]=h[i][j];
                        w[p]=we+1;
                        break;
                    }
                    else
                    {
                        we+=w[p];
                        if(we*s[p]>=ans)
                        {
                            ans=s[p]*we;
                            x=we;
                            y=s[p];
                            q.insert(x*(y-1));
                            q.insert(y*(x-1));
                            q.insert(x*y);
                        }
                        p--;
                    }
                }
            }
        }
        while(q.size()>2)
        {
            q.erase(q.begin());
        }
    }
    cout<<(*q.begin())<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值