CSU-ACM2017暑假集训比赛1 B - R2D2 and Droid Army

B - R2D2 and Droid Army

An army of n droids is lined up in one row. Each droid is described by m integers a1, a2, ..., am, where ai is the number of details of the i-th type in this droid's mechanism. R2-D2 wants to destroy the sequence of consecutive droids of maximum length. He has m weapons, the i-th weapon can affect all the droids in the army by destroying one detail of the i-th type (if the droid doesn't have details of this type, nothing happens to it).

A droid is considered to be destroyed when all of its details are destroyed. R2-D2 can make at most k shots. How many shots from the weapon of what type should R2-D2 make to destroy the sequence of consecutive droids of maximum length?

Input

The first line contains three integers n, m, k (1 ≤ n ≤ 105, 1 ≤ m ≤ 5, 0 ≤ k ≤ 109) — the number of droids, the number of detail types and the number of available shots, respectively.

Next n lines follow describing the droids. Each line contains m integers a1, a2, ..., am (0 ≤ ai ≤ 108), where ai is the number of details of the i-th type for the respective robot.

Output

Print m space-separated integers, where the i-th number is the number of shots from the weapon of the i-th type that the robot should make to destroy the subsequence of consecutive droids of the maximum length.

If there are multiple optimal solutions, print any of them.

It is not necessary to make exactly k shots, the number of shots can be less.

Example

Input

5 2 4
4 0
1 2
2 1
0 2
1 3

Output

2 2


Input

3 2 4
1 2
1 3
2 2

Output

1 3

Note

In the first test the second, third and fourth droids will be destroyed.

In the second test the first and second droids will be destroyed.

这题有两个重点:一是二分搜索,二是动态规划。二分搜索用来枚举最长的有效区间长度,动态规划用来算
出区间最大值(RMQ)。
二分搜索较易,主要解释区间最大值的算法。

设有数组 a[] f(i,j) 表示 a[] 中从第 i 个数起,连续 2j 个数中的最大值;例如数列 3 2 4 5 6 8 1 2 9 7 f(1,0) 表示第1个数起,长度为 20=1 的最大值,也就是 f(1,0)=3 。同理, f(1,2)=5 f(1,3)=8 f(2,0)=2 f(2,1)=4 …… 同时,我们可以发现 f(i,0) 是可以不加计算,直接得到的,它就等于 a[i] 的值,即 f(i,0)=a[i]
利用这个条件,可以方便地得出动态规划的初态。

同时,由 (i,j) 这个有序整数对的特殊含义可知, f(i,j) 对应的区间长度 l=2n,nN (这点很重要,后续的下标计算都建立在这个条件上)

现在设计动态规划所使用的数组。
已经知道存在某个区间的最大值 M 可由关系 f(i,j)=M 得到,于是设计这样的二维数组 dp[i][j] 来反映 f(i,j) 。对这个数组,存在如下的状态转移方程:

dp[i][j]=max(dp[i][j1],dp[i+2j1][j1])

循环过程中, j 位于外层, i 位于内层。

动态规划计算结束后,需要通过查询函数来对枚举的区间长度 mid 进行查询,从而判断这个区间长度是否最优。之前提到, f(i,j) 对应的区间长度 l=2n,nN ,所以查询前要对区间长度大于一,且区间长度为基数的区间进行处理(为了偷懒,将所有区间都进行处理,具体算法见代码中 rmq() 函数)。方法很简单,就是找出当前区间中包含的最大的长度为偶数的区间长度,以第一个元素为首,取出子区间 S,以最后一个元素为尾,取出子区间 T,例如对区间 3 2 4 5 6 取出 S:3 2 4 5 T:2 4 5 6 ,分别求出区间最大值,其中较大的就是原区间中的最大值。

最后就是注意区间起点和区间长度的保存。算法得到的区间起点就作为答案的特征值,针对这个起点,按照区间长度,将题中要求的 m 个数组中的最大值分别算出,得到机器人的每个部位(detail)需要打多少枪,把这些值依次输出即可。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, m, k, universalIndex;
int a[100005][6], b[100005][20][6];
//a用于保存机器人的信息,b用来动态规划。

int max(int a, int b){
    return a > b ? a : b;
}
void creatST(){
//用动态规划的方法算出特定区间中的最大值。

    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            b[i][0][j] = a[i][j];
        }
    }
    for(int j = 1; (1<<j) <= n; j++){//控制j的大小,不必自增至n
        for(int i = 1; i + (1<<j) - 1 <= n; i++){
            for(int k = 1; k <= m; k++){
                b[i][j][k] = max(b[i][j - 1][k], b[i + (1<<(j-1))][j - 1][k]);
            }
        }
    }
}
int rmq(int l, int r, int index){
//查出区间最大值,复杂度O(1)

    int z = 0;
    while( (1<<(z+1)) <= r - l + 1)//用这种算法对所有区间长度一视同仁,全部经过同一个计算过程后送入判断函数
        z++;
    return max(b[l][z][index], b[r - (1<<z) + 1][z][index]);
}
bool check(int mid){
//从前向后依次枚举长度为mid的区间,检查各个数组在这个区间中的
//区间最大值之和与k值的差别。

    for(int i = 1; i + mid - 1 <= n; i++){
        int cnt = 0;
        for(int j = 1; j <= m; j++){
            cnt += rmq(i,i + mid - 1,j);
        }
        if(cnt <= k){
            universalIndex = i;   //保存区间起点。
            return true;
        }
    }
    return false;
}
int main(){

    while(cin >> n >> m >> k){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                scanf("%d", &a[i][j]);
            }
        }
        creatST();
        //将输入的结果生成一个ST(Sparse Tree),用于区间最大值查询。

        int left = 1, right = n, mid, range;
        while(left <= right){
            mid = (left + right) / 2;
            if(check(mid)){       //区间过小或正好。
                range = mid;
                left = mid + 1;
            }
            else{                 //区间过大。
                right = mid - 1;
            }
        }
        for(int i = 1; i <= m; i++){
            if(i == 1)
                cout << rmq(universalIndex, universalIndex + range - 1, i);
            else
                cout << " " << rmq(universalIndex, universalIndex + range - 1, i);
        }
        cout << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值