FZU2234 牧场物语(双线DP)

FZU2234 牧场物语


小茗同学正在玩牧场物语。该游戏的地图可看成一个边长为n的正方形。 小茗同学突然心血来潮要去砍树,然而,斧头在小茗的右下方。 小茗是个讲究效率的人,所以他会以最短路程走到右下角,然后再返回到左上角。并且在路上都会捡到/踩到一些物品,比如说花朵,钱和大便等。 物品只能被取最多一次。位于某个格子时,如果格子上还有物品,就一定要取走。起点和终点上也可能有物品。 每种物品我们将为其定义一个价值,当然往返之后我们取得的物品的价值和越大越好。但是小茗同学正在认真地玩游戏,请你计算出最大的价值和。

Input
多组数据(<=10),处理到EOF。
第一行输入正整数N(N≤100),表示正方形的大小。
接下来共N行,每行N个整数Ai,j(|Ai,j|≤10^9),表示相应对应位置上物品的价值。值为0表示没有物品。

Output
每组数据输出一个整数,表示最大价值和。


Sample Input
2
11 14
16 12
Sample Output
53

思路

第一次做这种题,因为惯性思维,写了个从头dp到尾,标记已选物品价值,然后,从尾dp到头。

错误:原先思路是先选一条最优路,再选剩下的最优。仔细想想,这样做和贪心很类似,相当于对情况排了序。虽然也是用动态规划思想做的,但这是在一个局部的阶段问题下使用的,考虑的还是局部最优,不是整体最优。
可参考这个>特例

将原来一个人走完,再另一个人走,转变为同步走,多进程对每一步都考虑两个人的动态规划

可设:
dp[k][i][j]a在(x1,y1)b在(x2,y2),同时走了k步,此时的最大价值
x+y=k+1(第一步在1,1位置),有了这个等式,只需要一个人的一个坐标分量就能表达这个人的位置,节省空间消耗

(x1-1,y1)或(x1,y1-1),走了k-1步的状态,化简为
只取一个坐标分量x1-1,x1
(x2-1,y2) 或(x2,y2-1),走了k-1步的状态,化简为
只去一个坐标分量x2-1,x2

排列得4种状态
公式为
dp[k][i][j]=max( dp[k-1][i][j],dp[k-1][i-1][j-1],dp[k-1][i-1][j],dp[k-1][i][j-1] )+a[i][k+1-i]+a[j][k+1-j]
当i==j,则不加a[j][k+1-j]或者a[i][k+1-i]

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")//传说中的加速
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
#define ff first
#define ss second
#define ms(x) memset(x,0,sizeof(x))
typedef long long ll;
#define inf 0x3f3f3f3f
#define mod 1e9+7
#define MAX 1e5
ll a[101][101];
ll dp[2*101][101][101];
//dp[k][i][j]:i+j=k+1  1=<i,j<=n
//1<=i,j<=k
int main(){
    //ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);//使用scanf时不能用这个
    int n;
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%I64d",&a[i][j]);//windows %I64d;linux %lld
        memset(dp,-0x3f,sizeof(dp));//0x3f3f3f3f当作无穷大可用memset赋值
        //-1,0,正负0x3f可以,但不能用0x7fff'ffff,具体细节可参考网上资料
        dp[1][1][1]=a[1][1];
        for(int k=2;k<=2*n-1;k++){
            for(int i=1;i<=min(k,n);i++){//防止越界
                for(int j=1;j<=min(k,n);j++){

                    ll add=a[i][k+1-i]+a[j][k+1-j];//坐标变换

                    dp[k][i][j]=max(max(dp[k-1][i][j],dp[k-1][i-1][j-1]),
                                    max(dp[k-1][i-1][j],dp[k-1][i][j-1]));
                    if(i!=j)
                    dp[k][i][j]+=add;
                    else
                    dp[k][i][j]+=(add>>1);
                }
            }
        }
        printf("%I64d\n",dp[2*n-1][n][n]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值