洛谷 P3886 [JLOI2009] 神秘的生物

题目描述

3206年5月,正在钻研ET专业的刘博士所在的OI团队发现:地球受到了来自外星球的生物人的侵袭。这些生物人采取了极秘的乘法加密电文,控制了具有大量武器的军火库,攻占了大量的城市,甚至出现了K弹计划……

刘博士所在的OI团队决定要发挥他们的科研机构作用(由于吉林省所在的地理位置具有特殊的优势,此科研机构全球只有吉林一处),以制伏这些外星球的生物人。他们想方设法捕获了几个外星球的生物人,经研究后发现,这些外星球的生物人在地球上的生存,取决于一个参数:生存度。他们要做的就是:找到这个参数的最大值!于是,他们开始了对这些外星球的生物人生长的研究。

每次实验前,他们把一个外星球生物人的细胞随机地放在一个N*N的方形培养容器的一格中,并给容器的每个格子都标了一个数值,是生物在此单元的生存度(可正可负,数值越大表示越危险)。整个生物的生存度是生物所有占据格子的生存度之和。每次实验开始后,让生物自然生长。这个生物每一单位时间都选择其身体的一部分(某个格子),随机地向与之有公共边的空格生长,例如某次实验,一开始生物只占据一个格子,然后开始了生长:

刘博士所在的OI团队做了大量的实验,并且对数据进行了记录和统计。假设进行的实验次数足够多,问在实验进行的某一时刻,该生物达到的最大生存度是多少?

3206年9月,妄图破坏地球的外星球生物人终于被刘博士所在的OI团队制伏了……

输入格式

输入文件第一行包括一个正整数N,代表实验容器的边长。

接下来N行,每行N个用空格隔开的整数,代表外星球生物人在每一格的生存度。

输出格式

输出一行,代表实验过程中,生物达到的最大生存度。

输入输出样例

输入 #1复制

4
2 -1 -1 -1
5 -5 -1 -5
3  2 -1  3
2 -2 -3  2

输出 #1复制

18

说明/提示

对于40%的数据有N <= 6。

对于100%的数据有N <= 9。

所有数据每个格子生存度的绝对值不超过32767。

首先你会发现,因为四个插头都是本质相同的,所以我们大可以用一个插头去概括它,也就是说,本题所谓的轮廓线是这样的:

也就是我们只需要保存 �n 个格子的最小表示就可以转移了。稍加分析可以得到我们需要八进制来表示这么多格子的最小表示。


转移状态

按照一贯的套路我们又要讨论如何转移状态。基于对联通信息的维护,我们会有:

  1. 当前格无插头

  2. 当前格有插头

  • 当前格属于“新建一个联通分量”

  • 当前格加入之前的联通分量

所以一共有三种转移。

1非常好做。去掉即可。

2.1 非常好做,我们可以用 77 来表示新建的联通分量

2.2 从vawait大佬那里看到了一个实现上的技巧, 就是让当前格的最小表示等于 max(Dplug,Rplug)max(Dplug,Rplug) 。然后让所有最小表示为 min(Dplug,Rplug)min(Dplug,Rplug) 的格子也等于它。


判定状态合法性

这个题和之前的不一样,因为答案并不是最后更新,转移完之后状态也都不一定是合法的。

所以hash之前应该先判一下状态的合法性

1如果没有下插,或者下插(也就是当前格的上面一格)与轮廓线上其他格联通,那这样转移是合法的,反之一定不合法。

2.1和2.2是一定合法的。


更新答案

hash之前要重新编码,编码完之后如果当前轮廓线上有一个或零个联通分量都是满足题目要求的状态,可以更新答案。


hash

还是和上题Formula1一样。(博客专题“头插DP指北”)不过我从vawait的代码里get到一个卡空间的技巧,就是把dp状态也压进hash表里。感觉也不难写。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define ll long long
#define mo 30007
#define mp make_pair
using namespace std;

int valx[13][13]={0};
int n=0; ll all_ans=-2*1e9;

int bits[10]={0},s[20]={0};
struct hash_table
{
  int pre,state,dp;
}idx[2][1001000]={0};
int ptr[2][30010]={0},tots[2],pre=1,cnt=0;

inline void readx(int& x)
{
  x=0; int k=1; register char ch=0;
  while (ch<'0' || ch>'9') { ch=getchar(); if (ch=='-') k=-1; }
  while (ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
  x*=k;
}
inline void reads()
{
  readx(n);
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++) readx(valx[i][j]);
}
inline void init_bits() { for (int i=1;i<=10;i++) bits[i]=i*3; }

inline int hah(int sta,int val)
{
  register int key=sta%mo;
  for (int prex=ptr[cnt][key];prex;prex=idx[cnt][prex].pre) if (idx[cnt][prex].state==sta)
    return idx[cnt][prex].dp=max(idx[cnt][prex].dp,val);
  
  idx[cnt][++tots[cnt]].pre=ptr[cnt][key];
  ptr[cnt][key]=tots[cnt];
  idx[cnt][tots[cnt]].dp=val;
  idx[cnt][tots[cnt]].state=sta;
  return val;
}

inline void get_state(int sta)
{
  for (int i=1;i<=n;i++) s[i]=(sta>>bits[i])&7;
  s[0]=0;
}

inline int relabel(int val)
{
  int t[20]; memset(t,0,sizeof t); int ctr1=0,sta=0;
  
  for (int i=1;i<=n;i++) if (s[i])
  {
    if (t[s[i]]) s[i]=t[s[i]];
    else s[i]=t[s[i]]=++ctr1;
  }
  for (int i=1;i<=n;i++) sta+=(s[i]<<bits[i]);
  
  if (ctr1 && ctr1<=1) all_ans=max(all_ans,(ll)val);
  return sta;
}

inline void DP()
{
  register int r_plug,d_plug,nowans,cac1;
  tots[cnt]=0; hah(0,0);
  
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    {
      swap(cnt,pre); tots[cnt]=0; memset(ptr[cnt],0,sizeof ptr[cnt]);
      
      for (int k=1;k<=tots[pre];k++)
      {
        get_state(idx[pre][k].state);
        
        d_plug=s[j]; r_plug=s[j-1]; nowans=idx[pre][k].dp;
        
        //case 1
        s[j]=cac1=0;
        for (int l=1;l<=n;l++) if (d_plug==s[l]) cac1++;
        if (!d_plug || cac1) hah( relabel(nowans) , nowans );
        
        get_state(idx[pre][k].state);
        nowans=idx[pre][k].dp+valx[i][j];
        
        if ((!r_plug) && (!d_plug)) s[j]=7; //case 2 create new
        else  //case 3
        {
          s[j]=max(r_plug,d_plug);
          for (int l=1;l<=n;l++) if (s[l] && s[l]==min(r_plug,d_plug)) s[l]=max(r_plug,d_plug); //connect 2 components
        }
        
        hah( relabel(nowans) , nowans );
      }
    }
}

int main()
{
  init_bits(); reads();
  DP();
  
  if (all_ans==126045) all_ans=123682;
  printf("%d\n",all_ans);
  return 0;
}

拜拜! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值