P1914子矩陣
Accepted
标簽:
NOIP普及組2014
描述
給出如下定義:
-
子矩陣:從一個矩陣當中選取某些行和某些列交叉位置所組成的新矩陣(保持行與 列的相對順序)被稱為原矩陣的一個子矩陣。
例如,下面左圖中選取第 2、4 行和第 2、4、5 列交叉位置的元素得到一個 2*3 的子矩陣如右圖所示。
- 相鄰的元素:矩陣中的某個元素與其上下左右四個元素(如果存在的話)是相鄰的。
- 矩陣的分值:矩陣中每一對相鄰元素之差的絕對值之和。
本題任務:給定一個 n 行 m 列的正整數矩陣,請你從這個矩陣中選出一個 r 行 c 列的 子矩陣,使得這個子矩陣的分值最小,并輸出這個分值。
格式
輸入格式
第一行包含用空格隔開的四個整數 n,m,r,c,意義如問題描述中所述,每兩個整數之間用一個空格隔開。
接下來的 n 行,每行包含 m 個用空格隔開的整數,用來表示問題描述中那個 n 行 m 列的矩陣。
輸出格式
輸出共 1 行,包含 1 個整數,表示滿足題目描述的子矩陣的最小分值。
樣例1
樣例輸入1[複制]
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
樣例輸出1[複制]
6
樣例2
樣例輸入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
限制
對于 50%的資料,1 ≤ n ≤ 12, 1 ≤ m ≤ 12, 矩陣中的每個元素 1 ≤ a[i][j] ≤20;
對于 100%的資料,1 ≤ n ≤ 16, 1 ≤ m ≤ 16, 矩陣中的每個元素 1 ≤ a[i][j] ≤1000,1 ≤ r ≤ n, 1 ≤ c ≤ m。
時間限制:每一組測試資料1s。
提示
【輸入輸出樣例 1 說明】
該矩陣中分值最小的 2 行 3 列的子矩陣由原矩陣的第 4 行、第 5 行與第 1 列、第 3 列、 第 4 列交叉位置的元素組成,為
675566
,其分值為 |6 − 5| + |5 − 6| + |7 − 5| + |5 − 6| + |6 − 7| + |5 − 5| + |6 − 6| = 6。
【輸入輸出樣例 2 說明】
該矩陣中分值最小的 3 行 3 列的子矩陣由原矩陣的第 4 行、第 5 行、第 6 行與第 2 列、第 6 列、第 7 列交叉位置的元素組成,選取的分值最小的子矩陣為
9957888810
來源
NOIP2014 普及組
解析:
動态規劃+位運算。
基本思路:先确定行,再對列進行動态規劃。
1.假設 n=3,r=2,以二進制來表示每一行,則:
0 1 1 :表示標明1、2行
1 0 1 :表示標明1、3行
1 1 0 :表示標明2、3行
可以看到,所有的情況即為在長度為 n 的二進制中,存在 r 個 1 的情況。
這就讓我想起了曾經做過的 poj2435,題意是這樣的:給定一個數 x,求與 x 在二進制下含有相同個數的 1 的數中最小的數。
正好适合這道題。代碼見下:
x=n&(-n),t=n+x;
ans=t|((n^t)/x)>>2;
javascript:void(0)
2.确定行,對列進行動态規劃。
用 f[i][j] 表示以第 i 列結尾的 r 行 j 列的子矩陣的最優值,則:
初始化 f[i][1]
f[i][j]=max{f[k][j-1]+f[i][1]+get(k,i)}
代碼:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 16
using namespace std;
int n,m,r,c,a[maxn+5][maxn+5];
int b[maxn+5],f[maxn+5][maxn+5];
int ans=2000000000;
void work(int s)
{
int i,j,k,x,p;
for(b[0]=i=0;i<n;i++)
if(s&(1<<i))b[++b[0]]=i+1;
memset(f,90,sizeof(f));
for(i=1;i<=m;i++)
for(f[i][1]=0,j=2;j<=b[0];j++)
f[i][1]+=abs(a[b[j]][i]-a[b[j-1]][i]);
for(j=2;j<=c;j++)
for(i=j;i<=m;i++)
for(k=j-1;k<i;k++)
{
for(x=0,p=1;p<=b[0];p++)x+=abs(a[b[p]][k]-a[b[p]][i]);
f[i][j]=min(f[i][j],f[k][j-1]+f[i][1]+x);
}
for(i=c;i<=m;i++)ans=min(ans,f[i][c]);
}
int main()
{
int i,j,k;
scanf("%d%d%d%d",&n,&m,&r,&c);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
int s=0,e=0,x,t;
for(i=0;i<r;i++)s=s|(1<<i);
for(i=n-r;i<n;i++)e=e|(1<<i);
while(s<=e)
{
work(s);
x=s&(-s),t=s+x;
s=t|((s^t)/x)>>2;
}
printf("%d\n",ans);
return 0;
}