NOIP 2014 PJT4~NOIP 2016 PJT4

2133: 子矩阵(NOIP 2014 PJT4)

题目描述

给出如下定义:
1. 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵。
例如,下面左图中选取第22、44行和第22、44、55列交叉位置的元素得到一个2×32×3的子矩阵如右图所示。
 


的其中一个2×32×3的子矩阵是
 


2. 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
3. 矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。
本题任务:给定一个n�行m�列的正整数矩阵,请你从这个矩阵中选出一个r�行c�列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

输入

第一行包含用空格隔开的四个整数n,m,r,c�,�,�,�,意义如问题描述中所述,每两个整数之间用一个空格隔开。 接下来的n�行,每行包含m�个用空格隔开的整数,用来表示问题描述中那个n�行m�列的矩阵。

输出

一个整数,表示满足题目描述的子矩阵的最小分值。

样例输入 复制

5 5 2 3
9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1

样例输出 复制

6

提示

输入样例 2

7 7 3 3
7 7 7 6 2 10 5
5 8 8 2 1 6 2
2 9 5 5 6 1 7
7 9 3 6 1 7 8
1 9 1 4 7 8 8
10 5 9 1 1 8 10
1 3 1 5 4 8 6

输出样例 2

16

【输入输出样例1说明】
该矩阵中分值最小的22行33列的子矩阵由原矩阵的第44行、第55行与第11列、第33列、第44列交叉位置的元素组成,为
 


,其分值为:
|6−5| + |5−6| + |7−5| + |5−6| + |6−7| + |5−5| + |6−6| =6。
【输入输出样例2说明】
该矩阵中分值最小的3行3列的子矩阵由原矩阵的第44行、第55行、第66行与第22列、第66列、第77列交叉位置的元素组成,选取的分值最小的子矩阵为
 


【数据说明】
对于50%50%的数据,1≤n≤12,1≤m≤121≤�≤12,1≤�≤12,矩阵中的每个元素1≤aij≤201≤���≤20;
对于100%100%的数据,1≤n≤16,1≤m≤161≤�≤16,1≤�≤16,矩阵中的每个元素1≤aij≤1,000,1≤r≤n,1≤c≤m1≤���≤1,000,1≤�≤�,1≤�≤�。

时间限制: 1 秒  内存限制: 128 MB  提交: 21  AC: 18    [提交][状态]

#include       <map>
#include       <set>
#include     <stack>
#include     <cmath>
#include     <ctime>
#include     <queue>
#include    <cstdio>
#include    <vector>
#include    <string>
#include    <bitset>
#include   <cstring>
#include   <cstdlib>
#include  <iostream>
#include <algorithm>
using namespace std;
 
#ifndef unix
    #define lld "%I64d"
    #define llu "%I64u"
#else
    #define lld "%lld"
    #define llu "%llu"
#endif
 
#define FOR(a,b,c)  for(int (a)=b;(a)<=(c);++(a))
#define FORD(a,b,c) for(int (a)=b;(a)>=(c);--(a))
#define FORV(a,t,b) for(vector<t>::iterator a=b.begin();a!=b.end();++a)
#define MAX(a,b)                   a=max(a,b)
#define MIN(a,b)                   a=min(a,b)
#define BLA                      printf("\n")
#define pb                          push_back
#define mp                          make_pair
#define gc                            getchar
#define RT                             return
#define BB                             second
#define AA                              first
#define bk                              break
#define LINF             0x3f3f3f3f3f3f3f3fll
#define INF                        0x3f3f3f3f
#define eps                              1e-8
#define DINF                             1e20
 
//#define Generator
 
typedef long long           ll;
typedef unsigned            ui;
typedef unsigned long long ull;
typedef pair<int,int>      pii;
typedef pair<ll ,ll >      pll;
 
const int MAXN= 0;
const int MOD = 0;
 
template <class T> inline void CLR(T &g)             {T t;swap(t,g);}
template <class T> inline void CLR(T &g,int a){memset(g,a,sizeof g);}
template <class T> inline void CPY(T &a,T &b) {memcpy(a,b,sizeof a);}
template <class T> inline bool inr(T a,T b,T c)  {RT (a>=b && a<=c);}
inline int acc(int a,int b)                    {RT !!(a & (1<<b-1));}
inline int fil(int a,int b,int c)    {RT a & ~(1<<b-1) | (1<<b-1)*c;}
 
int N, M, K, Q, R, C;
 
int a[20][20];
int f[20][20];//f[i][j]表示 已经选择i列 上一列是j
int sum[20], d[20], csum[20][20];
int main()
{
    #ifndef Generator
    #ifndef ONLINE_JUDGE
    #endif
    #endif                                                         //真
    #ifdef Generator                                               //正
        freopen("input.txt","w",stdout);                           //有
        srand((ui)time(NULL));                                     //用
    #endif                                                         //的
    int T, o=0;                                                    //在
    scanf("%d%d%d%d", &N, &M, &R, &C);                             //这
    FOR(i, 1, N)                                                   //里
        FOR(j, 1, M)
            scanf("%d", &a[i][j]);
    int ans=INF;
    FOR(i, 1, (1<<N)-1){//枚举哪些行被选入矩阵
        int cnt=0;
        FOR(j, 1, N)
            if (acc(i, j)) d[++cnt]=j;
        if (cnt != R) continue;
        FOR(j, 1, M){
            sum[j]=0;
            FOR(k, 1, R-1)
                sum[j] += abs(a[d[k]][j]-a[d[k+1]][j]);
        }
        FOR(j, 1, M)
            FOR(k, j+1, M){
                csum[j][k]=0;
                FOR(l, 1, R)
                    csum[j][k] += abs(a[d[l]][j]-a[d[l]][k]);
            }
        CLR(f, 0x3f);
        f[0][0]=0;
        FOR(j, 1, C){
            FOR(k, 1, M){
                int tot=0;
                FOR(l, 0, k-1)
                    MIN(f[j][k], f[j-1][l]+csum[l][k]+sum[k]);
            }
        }
        FOR(j, 1, M)
            MIN(ans, f[C][j]);
    }
    printf("%d\n", ans);
    RT 0;
}

2161: 推销员(NOIP 2015 PJT4)

题目描述

阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N�家住户,第i�家住户到入口的距离为Si��米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X�家住户推销产品,然后再原路走出去。
阿明每走11米就会积累11点疲劳值,向第i�家住户推销产品会积累Ai��点疲劳值。阿明是工作狂,他想知道,对于不同的X�,在不走多余的路的前提下,他最多可以积累多少点疲劳值。

输入

第一行有一个正整数N�,表示螺丝街住户的数量。
接下来的一行有N�个正整数,其中第i�个整数Si��表示第i�家住户到入口的距离。数据保证S1≤S2≤…≤Sn<108�1≤�2≤…≤��<108。
接下来的一行有N�个正整数,其中第i�个整数Ai��表示向第i�户住户推销产品会积累的疲劳值。数据保证Ai<1000��<1000。

输出

输出N�行,每行一个正整数,第i行整数表示当X=i�=�时,阿明最多积累的疲劳值。

样例输入 复制

5
1 2 3 4 5
1 2 3 4 5

样例输出 复制

15
19
22
24
25

提示

输入样例 2

5
1 2 2 4 5
5 4 3 4 1

输出样例 2

12
17
21
24
27

说明

【输入输出样例1说明】
X=1�=1:向住户55推销,往返走路的疲劳值为5+55+5,推销的疲劳值为55,总疲劳值为1515。
X=2�=2:向住户4,54,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值为4+54+5,总疲劳值为5+5+4+5=195+5+4+5=19。
X=3�=3:向住户3,4,53,4,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值3+4+53+4+5,总疲劳值为5+5+3+4+5=225+5+3+4+5=22。
X=4�=4:向住户2,3,4,52,3,4,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值2+3+4+52+3+4+5,总疲劳值5+5+2+3+4+5=245+5+2+3+4+5=24。
X=5�=5:向住户1,2,3,4,51,2,3,4,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值1+2+3+4+51+2+3+4+5,总疲劳值5+5+1+2+3+4+5=255+5+1+2+3+4+5=25。
【输入输出样例2说明】
X=1�=1:向住户44推销,往返走路的疲劳值为4+44+4,推销的疲劳值为44,总疲劳值4+4+4=124+4+4=12。
X=2�=2:向住户1,41,4推销,往返走路的疲劳值为4+44+4,推销的疲劳值为5+45+4,总疲劳值4+4+5+4=174+4+5+4=17。
X=3�=3:向住户1,2,41,2,4推销,往返走路的疲劳值为4+44+4,推销的疲劳值为5+4+45+4+4,总疲劳值4+4+5+4+4=214+4+5+4+4=21。
X=4�=4:向住户1,2,3,41,2,3,4推销,往返走路的疲劳值为4+44+4,推销的疲劳值为5+4+3+45+4+3+4,总疲劳值4+4+5+4+3+4=244+4+5+4+3+4=24。或者向住户1,2,4,51,2,4,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值为5+4+4+15+4+4+1,总疲劳值5+5+5+4+4+1=245+5+5+4+4+1=24。
X=5�=5:向住户1,2,3,4,51,2,3,4,5推销,往返走路的疲劳值为5+55+5,推销的疲劳值为5+4+3+4+15+4+3+4+1,总疲劳值5+5+5+4+3+4+1=275+5+5+4+3+4+1=27。
【数据说明】
对于20%20%的数据,1≤N≤201≤�≤20;
对于40%40%的数据,1≤N≤1001≤�≤100;
对于60%60%的数据,1≤N≤10001≤�≤1000;
对于100%100%的数据,1≤N≤1000001≤�≤100000。

时间限制: 1 秒  内存限制: 128 MB  提交: 26  AC: 24    [提交][状态]

#include <iostream>
#include <cstdio>
#include <queue>
  
using namespace std;
  
const int MAXN = 1e5 + 100;
int n, a[MAXN], s[MAXN], far, b[MAXN];
  
struct node1 {
    int x, c;
    bool operator < (const node1 &T) const {
        return c < T.c;
    }
};
priority_queue<node1> q1;
struct node2 {
    int x, c;
    bool operator < (const node2 &T) const {
        return c < T.c;
    }
};
priority_queue<node2> q2;
  
void build() {
    while(!q1.empty()) q1.pop();
    while(!q2.empty()) q2.pop();
    for(int i=1; i<far; i++)
        if(!b[i])
            q1.push((node1){ i, a[i] });
    for(int i=far+1; i<=n; i++)
        if(!b[i])
            q2.push((node2){ i, (s[i]-s[far])*2+a[i] });
}
  
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
        scanf("%d", &s[i]);
    for(int i=1; i<=n; i++)
        scanf("%d", &a[i]);
    build();
    int sum = 0;
    for(int i=1; i<=n; i++) {
        int pos, maxn = 0;
        if(q1.empty()) {
            pos = q2.top().x, maxn = q2.top().c;
            q2.pop();
        } else if(q2.empty()) {
            pos = q1.top().x, maxn = q1.top().c;
            q1.pop();
        } else if(q1.top().c > q2.top().c) {
            pos = q1.top().x, maxn = q1.top().c;
            q1.pop();
        } else {
            pos = q2.top().x, maxn = q2.top().c;
            q2.pop();
        }
        // cout << pos << endl;
        b[pos] = 1;
        if(pos > far) {
            far = pos;
            build();
        }
        sum += maxn;
        printf("%d\n", sum);
    }
    return 0;
}

2165: 魔法阵(NOIP 2016 PJT4)

题目描述

六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有 m� 个魔法物品,编号分别为 1,2,…,m1,2,…,�。每个物品具有一个魔法值,我们用 Xi�� 表示编号为 i� 的物品的魔法值。每个魔法值 Xi�� 是不超过 n� 的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为 a,b,c,d�,�,�,� 的魔法物品满足 Xa<Xb<Xc<Xd,Xb−Xa=2(Xd−Xc)��<��<��<��,��−��=2(��−��),并且 Xb−Xa<(Xc−Xb)/3��−��<(��−��)/3 时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的 A� 物品,B� 物品,C� 物品,D� 物品。
现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的 A� 物品出现的次数,作为 B� 物品的次数,作为 C� 物品的次数,和作为 D� 物品的次数。

输入

第一行包含两个空格隔开的正整数 n,m�,�。 接下来 m� 行,每行一个正整数,第 i+1�+1 行的正整数表示 Xi��,即编号为 i� 的物品的魔法值。 保证 1≤n≤150001≤�≤15000,1≤m≤400001≤�≤40000,1≤Xi≤n1≤��≤�。每个 Xi�� 是分别在合法范围内等概率随机生成的。

输出

共 m� 行,每行 44 个整数。第 i� 行的 44 个整数依次表示编号为 i� 的物品作 为 A,B,C,D�,�,�,� 物品分别出现的次数。 保证标准输出中的每个数都不会超过 109109。每行相邻的两个数之间用恰好一个空格隔开。

样例输入 复制

30 8
1
24
7
28
5
29
26
24

样例输出 复制

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

提示

输入样例 2

15 15
1 
2 
3 
4 
5
6 
7 
8 
9
10
11
12
13
14
15

输出样例 2

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

【样例解释 11】
共有 55 个魔法阵,分别为:
物品 1,3,7,61,3,7,6,其魔法值分别为 1,7,26,291,7,26,29;
物品 1,5,2,71,5,2,7,其魔法值分别为 1,5,24,261,5,24,26;
物品 1,5,7,41,5,7,4,其魔法值分别为 1,5,26,281,5,26,28;
物品 1,5,8,71,5,8,7,其魔法值分别为 1,5,24,261,5,24,26;
物品 5,3,4,65,3,4,6,其魔法值分别为 5,7,28,295,7,28,29。
以物品 55 为例,它作为 A� 物品出现了 11 次,作为 B� 物品出现了 33 次,没有作为 C� 物品或者 D� 物品出现,所以这一行输出的四个数依次为 1,3,0,01,3,0,0。
此外,如果我们将输出看作一个 m� 行 44 列的矩阵,那么每一列上的 m� 个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。
【数据规模】

 

#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#define enter putchar(10)
#define debug(c,que) cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) cout << arr[i] << w;
#define speed_up() cin.tie(0),cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define Abs(x) ((x) > 0 ? (x) : -(x))
const int mod = 1e9 + 7;
inline int MOD(int x) {
    if(x < 0) x += mod;
    return x % mod;
}
namespace Newstd {
    char buf[1 << 21],*p1 = buf,*p2 = buf;
    inline int getc() {
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
    }
    inline int read() {
        int ret = 0,f = 0;char ch = getc();
        while (!isdigit(ch)) {
            if(ch == '-') f = 1;
            ch = getc();
        }
        while (isdigit(ch)) {
            ret = (ret << 3) + (ret << 1) + ch - 48;
            ch = getc();
        }
        return f ? -ret : ret;
    }
    inline void write(int x) {
        if(x < 0) {
            putchar('-');
            x = -x;
        }
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace Newstd;
using namespace std;
   
const int ma1 = 1.5e4 + 5,ma2 = 4e4 + 5;
int tong[ma1],a[ma2],matrix[ma2][5];
int n,m;
int main(void) {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif
    n = read(),m = read();
    for (register int i = 1;i <= m; ++ i) {
        a[i] = read();
        tong[a[i]] ++;
    }
    for (register int t = 1,sum;t * 9 < n; ++ t) {
        sum = 0;
        for (register int A = n - 9 * t - 1;A >= 1; -- A) {
            int B = A + 2 * t;
            int C = A + 8 * t + 1;
            int D = A + 9 * t + 1;
            sum += tong[C] * tong[D];
            matrix[A][1] += sum * tong[B],matrix[B][2] += sum * tong[A];
        }
        sum = 0;
        for (register int D = 9 * t + 2;D <= n; ++ D) {
            int A = D - 9 * t - 1;
            int B = A + 2 * t;
            int C = D - t;
            sum += tong[A] * tong[B];
            matrix[C][3] += sum * tong[D],matrix[D][4] += sum * tong[C];
        }
    }
    for (register int i = 1;i <= m; ++ i) {
        for (register int j = 1;j <= 4; ++ j) {
            printf("%d ",matrix[a[i]][j]);
        }
        enter;
    }
   
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值