poj 3274 自定义排序

题意:n头牛站一排,每个牛有k个属性,每个属性有两种取值:1或0。(1表示拥有该属性,0表示没有),用位来保存属性,属性≤30个。要求找一个牛的最长连续队伍(子段),这个队伍中拥有每个属性的牛的个数相同。

思路:看数据量n^2肯定不行,想不出来,看了眼官方的解法大致明白。

Consider the partial sum sequence of each of the k features built by taking the sum of all the values up to position i. The problem is equivalent to:Given an array s[n][k], find i,j, with the biggest separation for which s[ i ][l]-s[j][l] is constant for all l.
The problem is now to do this efficiently. Notice that s[ i ][l]-s[j][l] being constant for all l is equivalent to s[ i ][l]-s[j][l]=s[ i ][1]-s[j][1] for all l, which can be rearranged to become s[ i ][l]-s[ i ][1]=s[j][l]-s[j][1] for all l. Therefore, we can construct another array a[n][k] where a[ i ][j]=s[ i ][j]-s[ i ][1] and the goal is to find i and j with the biggest separation for which a[ i ][l]=a[j][l] for all l.
This can be done by sorting all the a[ i ] entries, which takes O(nklogn) time (although in practice rarely will all k elements be compared). Another alternative is to go by hashing, giving an O(nk) solution. Both solutions are fairly straightforward once the final array is constructed.

翻译过来就是:我们记录一个sum[i][j]数组,记录前i个牛的第j个属性和。我们用我们需要寻找sum中的两行,这两行中所有对应位的差都相等,则这段牛是符合条件的答案之一。所以我们用另一数组,来记录sum中对应行的各个元素之间的差异。我们记录c[i][j] = sum[i][j] - sum[i][0]这样只要c的两行对应相等即可。然后我们就找相隔最远的两个c的相等行即为所求。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 100005
int n,k;
struct node{
    int id;
    int d[32];
}s[N];
int cmp(node a,node b){
    for(int i = k-1;i>=0;i--){
        if(a.d[i] < b.d[i])
            return 1;
        if(a.d[i] > b.d[i])
            return 0;
    }
    return a.id < b.id;
}
int equal(int a,int b){
    for(int i = k-1;i>=0;i--)
        if(s[a].d[i] != s[b].d[i])
            return 0;
    return 1;
}
int main(){
    int i,j,x,res=0;
    scanf("%d %d",&n,&k);
    for(i = 1;i<=n;i++){
        scanf("%d",&x);
        s[i].id = i;
        for(j = 0;j<k;j++){
            s[i].d[j] = x&1;
            if(i>1)
                s[i].d[j] += s[i-1].d[j];
            x>>=1;
        }
    }
    for(i = 1;i<=n;i++){
        x = s[i].d[0];
        for(j = 0;j<k-1;j++)
            s[i].d[j] -= s[i].d[j+1];
        s[i].d[k-1] -= x;
    }
    clc(s[0].d,0);
    s[0].id = 0;
    sort(s,s+n+1,cmp);
    for(i = 0;i<=n;){
        for(j = i+1;j<=n&&equal(i,j);j++);
        res = max(res,s[j-1].id-s[i].id);
        i = j;
    }
    printf("%d\n",res);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值