魔改最小斯坦纳树的题。。。
首先我们发现颜色太多了非常不好处理,一种套路是随机把这些颜色映射到 (k) 种颜色,随机很多次来保证正确性。最后的正确率大概是:
[1-(1-frac{k!}{k^k})^C
]
其中 (C) 为随机次数。
好了现在我们只有 (kle 7) 种颜色了。考虑类似最小斯坦纳树的设状态:设 (f[i][j][s]) 表示包括第 (i) 行第 (j) 列的状态为 (s) 的连通块的最小代价。然后由于是点权所以方程式要稍微变动一下。
代码:
#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
using namespace std;
mt19937 rnd(0);
const int N=20,INF=1<<29;
int dx[10]={0,-1,0,1,0};
int dy[10]={0,0,1,0,-1};
int val[N][N],n,m,k,col[N][N],ans=INF,D[N*N],a[N][N],vis[N][N],f[N][N][2000];
priority_queue <pair<int,pair<int,int> > > q;
void init()
{
scanf("%d %d %d",&n,&m,&k);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&val[i][j]);
}
void Dijkstra(int s)
{
memset(vis,0,sizeof(vis));
while(!q.empty())
{
pair<int,int> p=q.top().second;q.pop();
if(vis[p.first][p.second])
continue;
vis[p.first][p.second]=1;
for (int i=1;i<=4;i++)
{
int tx=p.first+dx[i],ty=p.second+dy[i];
if(tx<1||ty<1||tx>n||ty>m||col[tx][ty]==-1)
continue;
if(f[tx][ty][s]>f[p.first][p.second][s]+val[tx][ty])
{
f[tx][ty][s]=f[p.first][p.second][s]+val[tx][ty];
q.push(make_pair(-f[tx][ty][s],make_pair(tx,ty)));
}
}
}
}
void work()
{
int T=300;
while(T--)
{
for (int i=0;i<n*m;i++)
D[i]=rnd()%k;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
col[i][j]=(a[i][j]!=-1)?D[a[i][j]]:-1;
int Max_N=1<<k;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int l=0;l<Max_N;l++)
f[i][j][l]=INF;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if(col[i][j]!=-1)
f[i][j][1<<col[i][j]]=val[i][j];
for (int s=1;s<Max_N;s++)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if(col[i][j]==-1) continue;
for (int sub=(s-1)&s;sub;sub=(sub-1)&s)
f[i][j][s]=min(f[i][j][sub]+f[i][j][s^sub]-val[i][j],f[i][j][s]);
if(f[i][j][s]!=INF)
q.push(make_pair(-f[i][j][s],make_pair(i,j)));
}
Dijkstra(s);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
ans=min(ans,f[i][j][Max_N-1]);
}
printf("%d
",ans==INF?-1:ans);
}
int main()
{
init();
work();
return 0;
}