传送门
随便抓了一道题来复习轮廓线DP。
听说和【JLOI2009 神秘的生物】本质上是一样的,没看,不清楚。
题解:
给一个棋盘图,每个格子有权值,求权值之和最小的连通块。
没什么难度,熟悉轮廓线的可以一眼出正解。
压一下轮廓线上点的连通情况,由于转移是四连通,所以我们只能用最小表示法。
最坏情况就是五列拉下来,有5个连通块。
直接上8进制压一下状态就行了。众所周知,在轮廓线DP中,最小表示法有一大堆无用状态,所以直接上哈希表优化一下就行了。
转移的话,我们直接考虑选不选这个格子,不选它就判断一下会不会导致它上方的格子所在连通块绝后。如果要选择,就看一下合并连通块还是新建连通块。
每次更新状态都要重新 O ( m ) O(m) O(m)标号,复杂度 O ( ⌊ m + 1 2 ⌋ m m 2 n ) O(\lfloor\frac{m+1}{2}\rfloor^mm^2 n) O(⌊2m+1⌋mm2n),但是无用状态实在太多了。。。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
class Map{
private:
static cs int mogic=1898579;
inline int locate(int k)cs{
int h=k%mogic;
while(key[h]!=-1&&key[h]!=k)h=h+1==mogic?0:h+1;
return h;
}
public:
int key[mogic],val[mogic];
int st[mogic],tp,waste;
Map(){memset(key,-1,sizeof key);tp=0;}
inline void clear(){
for(int re i=1;i<=tp;++i){
key[st[i]]=-1;
val[st[i]]=0;
}tp=0;
}
cs int &operator[](int k)cs{
int h=locate(k);
return key[h]==k?val[h]:waste;
}
int &operator[](int k){
int h=locate(k);
if(key[h]==-1){
key[h]=k,val[h]=0;
st[++tp]=h;
}
return val[h];
}
bool find(int k)cs{return key[locate(k)]==k;}
};
class CheapestIsland{
private:
static cs int SIZE=6e5+5;
int n,m,ans;
int w[15][15];
int state[SIZE],va[SIZE],tp;Map mp;
inline int get(int s,int t){if(t<0)return 0;return s>>(t*3)&7;}
inline int set(int s,int t,int st){if(t<0)return s;return s-((get(s,t)-st)<<(t*3));}
inline bool other(int s,int st){
bool flag=false;
for(int re i=0;i<m;++i){
if(get(s,i)==st)if(flag)return true;
else flag=true;
}
return false;
}
inline void relable(int &s){
int tot=0,id[15]={0};
for(int re i=0;i<m;++i){
int t=get(s,i);if(!t)continue;
s=set(s,i,id[t]?id[t]:id[t]=++tot);
}
}
inline bool final_check(int s){
int ct=0;
for(int re i=0;i<m;++i){
ct=ct||get(s,i)==1;
if(get(s,i)>1)return false;
}
return ct;
}
inline void update(int s,int val){
relable(s);if(final_check(s))ans=std::min(ans,val);
if(mp.find(s))mp[s]=std::min(mp[s],val);
else mp[s]=val;
}
public:
CheapestIsland(){}
int minCost(std::vector<std::string> cell){
n=cell.size();ans=0,m=1;
for(int re i=1;i<cell[0].size();++i)if(cell[0][i]==' ')++m;
for(int re i=0;i<n;++i){
std::stringstream ss(cell[i]);
for(int re j=0;j<m;++j)ss>>w[i][j];
}
update(0,0);
for(int re i=0;i<n;++i)
for(int re j=0;j<m;++j){
for(int re k=1;k<=mp.tp;++k){
state[k]=mp.key[mp.st[k]];
va[k]=mp.val[mp.st[k]];
}tp=mp.tp;mp.clear();
for(int re k=1;k<=tp;++k){
int s=state[k],v=va[k];
int t1=get(s,j-1),t2=get(s,j);
if(!t2||other(s,t2))update(set(s,j,0),v);
if(!t1&&!t2)update(set(s,j,7),v+w[i][j]);
else {
int id=std::max(t1,t2);s=set(s,j,id);
for(int re t=0;t<m;++t)
if(get(s,t)&&get(s,t)==std::min(t1,t2))
s=set(s,t,id);
update(s,v+w[i][j]);
}
}
}
return ans;
}
};
#ifdef zxyoi
CheapestIsland Solver;
signed main(){
std::cout<<Solver.minCost(
{"-1 -1 2 -1 -1",
"-1 -1 3 -1 -1",
"-1 -1 1 -1 -1",
"99 99 99 99 99",
"-1 -1 -1 -1 -1",
"-1 -1 -1 -1 -1"}
)<<"\n";
return 0;
}
#endif