题目描述
WNJXYK and DIDIDI are friends. They are thinking about how to make money all the time.
One day, they go to a gold mine and want to dig some gold from it. But the gold mine they went is strange and it has some strict rules for people who wants to dig gold. They want to get the maximum value of gold and obey the rules at the same time. Now, they have got all information of the gold mine and give this problem to you.
The gold mine can be considered as a N×N gird and every unit has its own value. If you dig gold in one unit and this unit will become empty and you can get all its value. If you want to dig gold from it, you must obey the following rules:
1.You must dig gold in every row.
2.In each row, you must dig K continuous units. (No more than K and no less than K units).
3.You can’t dig the units and let the empty units form a passage the can go from the left side of the gold mine to the right side of it.
4.You can’t dig the units and let the empty units form a passage the can go from the top of the gold mine to the bottom of the it.
Can you calculate the maximum value of the gold they can get without break the rules?
输入
The first line of input contains a positive integer T telling you there are T test cases followed.
In each Test Case, there are two positive integers N,K in the first line. Following N lines, there are N integers in every line telling the value of each units. We guarantee that the answer of each Test Case is less than 1e9.
输出
You need to output one line for each Test Case. “Case #X: Y” means the answer of Xth Test Case is Y.
样例输入
1
6 3
1 2 3 4 5 3
1 6 4 5 1 2
7 3 8 2 1 1
2 1 6 7 3 4
3 1 4 4 4 4
5 6 7 1 1 1
样例输出
Case #1: 89
提示
2≤N≤250 , 1≤K≤N⁄2
In the first sample, they should dig gold mine in this way.(X means they should dig this unit)
题意
给你一个 n * n 的数字矩阵,每一行可以选长度为 k 的连续序列,让你求这 n 行连续序列的最大和为多少
(要求,被选中这些数,不能在同一行,也不能再同一列)
思路
dp [ i ] [ j ] [ p ] : 表示第 i 行 以 j 开头长度为 k 的区间与哪个边界联通
p : (0)无 \ (1)连右 \ (2)连上 \ (3)连上连右 \ (4)连左 \ (6)连左连上
因为最后的不能有一列的数都被选中,所以我们最后取 p 值为(0,1,4) 的来更新答案
我们每次以上一行的 dp 来更新这一行的 dp (第一行例外,单独更新)
(yysy,看学长代码看的眼疼/(ㄒoㄒ)/~~,还不会,看了好久,也就理解了个大概,dp对我来说还是难了,等我再学习学习吧,到时候有机会再来完善这篇博客)
代码
#include<iostream>
#include<string>
#include<map>
#include<set>
//#include<unordered_map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<fstream>
#define X first
#define Y second
#define best 131
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps=1e-7;
const double pai=acos(-1.0);
const int N=2e4+10;
const int maxn=255;
const int mod=1e9+7;
ll dp[maxn][maxn][8];
//000(0)无 001(1)连右 010(2)连上 011(3)连上连右 100(4)连左 110(6)连左连上
ll x,a[maxn][maxn],sum[maxn][maxn];
bool check(int l,int r,int k)
{
if(l>r) swap(l,r);
if(l+k-1<r) return 1;
else return 0;
}
int main( )
{
int t,n,k,tot=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%lld",&x);
sum[i][j]=sum[i][j-1]+x;
}
}
memset(dp,-INF,sizeof(dp));
dp[1][1][6]=sum[1][k];
dp[1][n-k+1][3]=sum[1][n]-sum[1][n-k];
for(int i=2;i<=n-k;i++)
{
dp[1][i][2]=sum[1][i+k-1]-sum[1][i-1];
}
//以上为第一行的赋值
for(int i=2;i<=n;i++)
{
for(int j=1;j<=n-k+1;j++)
{
if(j>k) //与最左边的区间块没有连接
{
for(int p=0;p<=6;p++)
{
if(p==5) continue;
dp[i][1][4]=max(dp[i][1][4],dp[i-1][j][p]+sum[i][k]);
}
}
else与最左边的区间块有连接
{
dp[i][1][4]=max(dp[i][1][4],max(dp[i-1][j][0],dp[i-1][j][4])+sum[i][k]);
dp[i][1][6]=max(dp[i][1][6],max(dp[i-1][j][2],dp[i-1][j][6])+sum[i][k]);
}
if(j+k-1<n-k+1)//与最右边的区间块没有连接
{
for(int p=0;p<=6;p++)
{
if(p==5) continue;
dp[i][n-k+1][1]=max(dp[i][n-k+1][1],dp[i-1][j][p]+sum[i][n]-sum[i][n-k]);
}
}
else//与最右边的区间块有连接
{
dp[i][n-k+1][1]=max(dp[i][n-k+1][1],max(dp[i-1][j][0],dp[i-1][j][1])+sum[i][n]-sum[i][n-k]);
dp[i][n-k+1][3]=max(dp[i][n-k+1][3],max(dp[i-1][j][2],dp[i-1][j][3])+sum[i][n]-sum[i][n-k]);
}
}
for(int j=2;j<=n-k;j++)
{
for(int q=1;q<=n;q++)
{
for(int p=0;p<=6;p++)
{
if(p==5) continue;
if(check(j,q,k))// j 开头的区间与 q 开头的区间没有接触
{
dp[i][j][0]=max(dp[i][j][0],dp[i-1][q][p]+sum[i][j+k-1]-sum[i][j-1]);
}
else// j 开头的区间与 t 开头的区间有接触
{
dp[i][j][p]=max(dp[i][j][p],dp[i-1][q][p]+sum[i][j+k-1]-sum[i][j-1]);
}
}
}
}
}
ll ans=0;
for(int i=1;i<=n-k+1;i++)
{
for(int p=0;p<=6;p++)
{
if(p==5||p==2||p==3||p==6) continue;//除去成列的情况
ans=max(ans,dp[n][i][p]);
}
}
printf("Case #%d: %lld\n",++tot,ans);
}
return 0;
}