题意:给出一个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;
}