[状压DP]JZOJ 3737 挖宝藏(treasure)
分析
这题的思路很妙!
考虑一层的
设f[x][y][S]表示包含x,y的区域中宝藏集合为S的最小代价
那么转移有f[x][y][S]=min{f[x][y][S']+f[x][y][S^S']-a[x][y] S'≠Ø
然后从各个方向转移过来
容易发现第二种转移可以用SPFA松弛
那么多层的怎么办?首先多开一维记录层数(其实可以滚动),然后有f[h-1][x][y][1]=f[h][x][y][S]+a[h-1][x][y] (S是取得下层所有宝藏的集合)
我们设一个特殊的宝藏0号作为下面所有宝藏,这样就很方便了!
#pragma GCC optimize(2) #include <iostream> #include <cstdio> #include <queue> #include <cstring> #define rint register int using namespace std; const int N=11; struct Treasure { int x,y; Treasure operator + (Treasure b) {return (Treasure){x+b.x,y+b.y};} }t[N][N],d[4]={(Treasure){0,1},(Treasure){0,-1},(Treasure){1,0},(Treasure){-1,0}}; int f[N][N][N][1<<N],a[N][N][N],k[N]; bool vis[N][N]; int h,n,m; bool Check(Treasure x) {return x.x<1||x.x>n||x.y<1||x.y>m;} void SPFA(int h,int S,Treasure pos) { int sum=0,cnt=0; deque<Treasure> q; while (!q.empty()) q.pop_back(); q.push_back(pos);vis[pos.x][pos.y]=1;sum+=f[h][pos.x][pos.y][S];cnt++; while (!q.empty()) { Treasure u=q.front();q.pop_front(); if (f[h][u.x][u.y][S]>sum/cnt) { q.push_back(u);continue; } sum-=f[h][u.x][u.y][S];cnt--; for (int i=0;i<4;i++) { Treasure v=u+d[i]; if (Check(v)) continue; if (f[h][v.x][v.y][S]>f[h][u.x][u.y][S]+a[h][v.x][v.y]) { f[h][v.x][v.y][S]=f[h][u.x][u.y][S]+a[h][v.x][v.y]; if (!vis[v.x][v.y]) { if (!q.empty()&&f[h][q.front().x][q.front().y][S]>f[h][v.x][v.y][S]) q.push_front(v); else q.push_back(v); sum+=f[h][v.x][v.y][S];cnt++; } vis[v.x][v.y]=1; } } vis[u.x][u.y]=0; } } void Solve() { memset(f,0x7f,sizeof f); for (rint i=1;i<=n;i++) for (rint j=1;j<=m;j++) f[h][i][j][1]=a[h][i][j]; for (rint i=h;i;i--) { for (rint j=1;j<=k[i];j++) f[i][t[i][j].x][t[i][j].y][1<<j]=a[i][t[i][j].x][t[i][j].y]; for (rint S=1,mxn=1<<k[i]+1;S<mxn;S++) for (rint x=1;x<=n;x++) for (rint y=1;y<=m;y++) { for (rint S1=1;S1<S;S1++) if ((S&S1)==S1) f[i][x][y][S]=min(f[i][x][y][S],f[i][x][y][S1]+f[i][x][y][S^S1]-a[i][x][y]); SPFA(i,S,(Treasure){x,y}); } for (rint x=1;x<=n;x++) for (rint y=1;y<=m;y++) f[i-1][x][y][1]=f[i][x][y][(1<<k[i]+1)-1]+a[i-1][x][y]; } } int main() { freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); scanf("%d%d%d",&h,&n,&m); for (int i=1;i<=h;i++) for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) scanf("%d",&a[i][j][k]); for (int i=1;i<=h;i++) { scanf("%d",&k[i]); for (int j=1;j<=k[i];j++) scanf("%d%d",&t[i][j].x,&t[i][j].y); } Solve();z int ans=2147483647; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) ans=min(ans,f[0][i][j][1]); printf("%d",ans); }
在日渐沉没的世界里,我发现了你。