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;
}