Educational Codeforces Round 80 (Rated for Div. 2) 二分 + 状压

13 篇文章 0 订阅
6 篇文章 0 订阅

传送门

文章目录

题意:

给你 n n n个长度为 m m m的数组,选出两个来,让他们每一位取 m a x max max构成新数组 b b b,让后最大化 b b b的最小值。

思路:

看到 m = 8 m=8 m=8,也就是每个数组长度为 m m m,很容易想到状压。由于要最大化最小值,我们不妨二分这个最小值,让后将 n n n个数组状压成一个二进制, > = m i d >=mid >=mid的位置为 1 1 1,否则为 0 0 0。这个时候我们只需要找出两个数使其取或后 [ 0 , m − 1 ] [0,m-1] [0,m1]的每一位都是 1 1 1即可。
我们发现只需要记录一下当前状态对应的编号,让后 2 8 ∗ 2 8 2^{8}*2^{8} 2828枚举状态,让后判断 ( i ∣ j ) = = ( 1 < < m ) − 1 (i|j)==(1<<m)-1 (ij)==(1<<m)1即可。
复杂度 O ( m a x ( n m , 2 2 m ) ∗ l o g ( 1 e 9 ) ) O(max(nm,2^{2m})*log(1e9)) O(max(nm,22m)log(1e9))
如果 m m m很大怎么办?有没有更好的方法呢?当然是有的。
假设我们知道了状态 110101 110101 110101,那么我们只需要看一下是否存在 001010 001010 001010即可。而这个状态有可能是某个状态的子集,我们不能对于每数都将其子集暴力的加入,这样复杂度会爆掉的,看到大佬有一个好的方法来递推求子集。我们从 ( 1 < < m ) − 1 (1<<m)-1 (1<<m)1开始,记为 n o w now now,让后遍历 [ 0 , m − 1 ] [0,m-1] [0,m1],记为 j j j,看一下 n o w ∣ ( 1 < < j ) now|(1<<j) now(1<<j)是否存在,存在的话就说明他是 n o w ∣ ( 1 < < j ) now|(1<<j) now(1<<j)的一个子集,就直接继承 n o w ∣ ( 1 < < j ) now|(1<<j) now(1<<j)的信息即可。这样递推的复杂度为 2 m ∗ m 2^m*m 2mm,显然更加的优秀。

复杂度 O ( m a x ( n m , 2 m ∗ m ) ∗ l o g ( 1 e 9 ) ) O(max(nm,2^{m}*m)*log(1e9)) O(max(nm,2mm)log(1e9))

//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m;
int a[N][10];
int id[1010],ans1,ans2;
bool flag;

bool check(int mid)
{
    memset(id,0,sizeof(id));
    for(int i=0;i<n;i++)
    {
        int now=0;
        for(int j=0;j<m;j++)
            if(a[i][j]>=mid) now+=(1<<j);
       id[now]=i+1;
    }
    int tot=(1<<m);
    for(int i=tot-1;i>=0;i--)
        for(int j=0;j<m;j++)
            if(id[i|(1<<j)])
                id[i]=id[i|(1<<j)];
    for(int i=tot-1;i>=0;i--)
        if(id[i]&&id[i^(tot-1)])
        {
            ans1=id[i];
            ans2=id[i^(tot-1)];
            return true;
        }
    return false;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&a[i][j]);
    int l=0,r=1e9,ans;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)) l=mid+1;
        else r=mid-1;
    }
    printf("%d %d\n",ans1,ans2);


	return 0;
}
/*

*/

复杂度 O ( m a x ( n m , 2 2 m ) ∗ l o g ( 1 e 9 ) ) O(max(nm,2^{2m})*log(1e9)) O(max(nm,22m)log(1e9))

//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m;
int a[N][10];
int id[1010],ans1,ans2;
bool flag;

bool check(int mid)
{
    memset(id,0,sizeof(id));
    for(int i=0;i<n;i++)
    {
        int now=0;
        for(int j=0;j<m;j++)
            if(a[i][j]>=mid) now+=(1<<j);
       id[now]=i+1;
    }
    for(int i=0;i<(1<<m);i++)
        for(int j=0;j<(1<<m);j++)
            if(id[i]&&id[j]&&(i|j)==((1<<m)-1))
            {
                ans1=id[i],ans2=id[j];
                return true;
            }
    return false;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&a[i][j]);
    int l=0,r=1e9,ans;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)) l=mid+1;
        else r=mid-1;
    }
    printf("%d %d\n",ans1,ans2);


	return 0;
}
/*

*/




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值