SGU 167 I-country(DP)

题意:给出一个n×m的矩阵,要在矩阵里选K个元素,另这些元素的和尽量大,并且任意两个元素相互可达并且它们之间到达的路径不能走两种以上的方向(比如每步只能走上或右)。

思路:一个非常非常恶心的dp……由于题中给出的要求,我们选的这个区域是有一定限制的,那就是这个图形不能凹进去,如果凹进去,一定会有两个元素之间要走至少3个方向才能到达。用dp[i][j][k][sx][sy][l]来表示状态,i表示前i行,j、k表示第i行选择j~k之间的元素,l表示当前选择元素的个数,sx和sy是用来表示当前行的延伸状态,用sx==0表示当前图形最左端可以向左延伸(即下一行可以选择1~j-1的元素,下面类似),sx==1表示只能向右延伸,用sy==0表示当前图形最右端可以向右延伸,sy==1表示只能向左延伸。记录路径的话,之前想暴力点每个状态记录准确的前一个状态,但是算了下,会爆内存,无奈,只好只记录了j和k,查找路径的时候在遍历一遍。。。感觉写查找路径比写状态转移还蛋疼……


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
struct Node
{
    int i,j,k,sx,sy,l,val;
    Node(){}
    Node(int a,int b,int c,int d,int e,int f,int g)
    {i=a;j=b;k=c;sx=d;sy=e;l=f;val=g;}
}node;
int a[20][20],sum[20][20],dp[16][16][16][2][2][230];
int pre[16][16][16][2][2][230][2];
int n,m,K;
void cal(int i,int j,int k,int sx,int sy,int l)
{
    int x=1,y=m;
    if(sx==1) x=j;
    if(sy==1) y=k;
    int sl,sr,tmp;
    for(int a=x;a<=k;++a)
        for(int b=max(a,j);b<=y;++b)
        {
            if(b-a+1+l>K) continue;
            if(a>j) sl=1; else sl=sx;
            if(b<k) sr=1; else sr=sy;
            tmp=dp[i][j][k][sx][sy][l]+sum[i+1][b]-sum[i+1][a-1];
            if(dp[i+1][a][b][sl][sr][b-a+l+1]<=tmp)
            {
                dp[i+1][a][b][sl][sr][b-a+l+1]=tmp;
                pre[i+1][a][b][sl][sr][b-a+l+1][0]=j;
                pre[i+1][a][b][sl][sr][b-a+l+1][1]=k;
                if(b-a+l+1==K&&tmp>node.val)
                    node=Node(i+1,a,b,sl,sr,b-a+l+1,tmp);
            }
        }
}
void getpath(int x,int l,int r,int sx,int sy,int cnt,int val)
{
    if(cnt==0) return ;
    int L=pre[x][l][r][sx][sy][cnt][0];
    int R=pre[x][l][r][sx][sy][cnt][1];
    if(L!=-1&&R!=-1&&x!=1)
    for(int i=0;i<=sx;++i)
        for(int j=0;j<=sy;++j)
        {
            if(dp[x-1][L][R][i][j][cnt-(r-l+1)]+sum[x][r]-sum[x][l-1]==val)
            {
                getpath(x-1,L,R,i,j,cnt-(r-l+1),val-sum[x][r]+sum[x][l-1]);
                break;
            }
        }
    for(int y=l;y<=r;++y)
        printf("%d %d\n",x,y);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d%d%d",&n,&m,&K))
    {
        for(int i=1;i<=n;++i)
        {
            sum[i][0]=0;
            for(int j=1;j<=m;++j)
            {
                scanf("%d",&a[i][j]);
                sum[i][j]=sum[i][j-1]+a[i][j];
            }
        }
        memset(dp[1],0xff,sizeof(dp[1]));
        node.val=-1;
        for(int i=1;i<=m;++i)
            for(int j=i;j<=m;++j)
                if(j-i+1<=K)
                {
                    dp[1][i][j][0][0][j-i+1]=sum[1][j]-sum[1][i-1];
                    pre[1][i][j][0][0][j-i+1][0]=pre[1][i][j][0][0][j-i+1][1]=-1;
                    if(j-i+1==K&&sum[1][j]-sum[1][i-1]>node.val)
                        node=Node(1,i,j,0,0,j-i+1,sum[1][j]-sum[1][i-1]);
                }
        for(int i=1;i<n;++i)
        {
            memset(dp[i+1],0xff,sizeof(dp[i+1]));
            for(int j=1;j<=m;++j)
                for(int k=j;k<=m;++k)
                    if(k-j+1<=K)
                    {
                        dp[i+1][j][k][0][0][k-j+1]=sum[i+1][k]-sum[i+1][j-1];
                        pre[i+1][j][k][0][0][k-j+1][0]=pre[i+1][j][k][0][0][k-j+1][1]=-1;
                        if(k-j+1==K&&sum[i+1][k]-sum[i+1][j-1]>node.val)
                            node=Node(i+1,j,k,0,0,k-j+1,sum[i+1][k]-sum[i+1][j-1]);
                    }
            for(int j=1;j<=m;++j)
             for(int k=j;k<=m;++k)
              for(int l=k-j+1;l<K;++l)
              {
                if(dp[i][j][k][0][0][l]!=-1) cal(i,j,k,0,0,l);
                if(dp[i][j][k][1][0][l]!=-1) cal(i,j,k,1,0,l);
                if(dp[i][j][k][0][1][l]!=-1) cal(i,j,k,0,1,l);
                if(dp[i][j][k][1][1][l]!=-1) cal(i,j,k,1,1,l);
              }
        }
        if(K)
        {
            printf("Oil : %d\n",node.val);
            getpath(node.i,node.j,node.k,node.sx,node.sy,K,node.val);
        }
        else printf("Oil : 0\n");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值