upc-Gold Mine(dp)

题目描述

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值