这是本菜鸡写过的最难的斯坦纳树题了。。。干脆叫它斯坦纳树终结者好了
题解
随机化的部分类似于这道题
只不过这里是要求中位数最小,那道题是要求和最小
我们可以二分这个中位数mid
把所有权值小于等于mid的格子设一个新权值-1,把大于mid的格子设为1
采用结构体DP,同时求出优先格子数最少,再考虑最小化权值和的DP值
如果当前的最小权值和是小于等于0的,则说明我们至少选了一半以上的小于mid的数,即mid可以取到更小的值
于是我们随机了颜色的分配方案之后,利用二分来求出当前颜色分配方案的最优解
虽然我们二分的是中位数,但是我们在验证的时候是优先了格子数最少的,所以不会出现解变劣的情况
于是总体的思路就是
随机颜色的分配方案,二分+斯坦纳树求出最优解
但是我写题的时候脑抽了,我当时的思路是
二分中位数,随机+斯坦纳树判断当前中位数是否可行
这个思路看起来很对(看起来就很离谱),但是它有一个致命的问题
随机100次颜色方案+直接判断解是否可行,它的正确率为98%
随机100次颜色方案+二分找最优解,它的正确率依然为98%
但是
二分找最优解+随机100次颜色方案判断,它的正确率会暴跌到(98%)^(log10^6)≈67%
这是完全无法接受的
问题就在于,你二分的过程中,每一步都得走对,都要找到最优的颜色方案
而随机100次找到最优的颜色分配方案的正确率为98%
要么提高随机次数,要么……写正解
所以还是写正解吧:(随机了150次)
upd:点权斯坦纳树可以不用在一开始加上当前点点权,到最后算答案的时候再来加会好写很多
upd:build之后可以直接判断K种颜色是否都有分配到它的颜色,提前判断出无解的情况,会快很多
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 60005
#define M 3005
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
int mp[250][250],a[250][250],col[250][250],val[250][250];
struct node{
int x,y;
node(){}
node(int _x,int _y){x=_x;y=_y;}
node operator + (const node &t)const{return node(x+t.x,y+t.y);}
bool operator < (const node &t)const{return x<t.x||(x==t.x&&y<t.y);}
}f[235][235][1<<5],ans,lans;
int n,m,K;
int id[N],tmp[N];bool vs[15];
bool build()
{
int i,j;
memset(vs,0,sizeof(vs));
random_shuffle(id+1,id+n*m+1);
for(i=1;i<=n*m;i++)tmp[id[i]]=rand()%K;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++){
if(a[i][j]==-1)col[i][j]=-1;
else col[i][j]=tmp[a[i][j]],vs[col[i][j]]=1;
}
for(i=0;i<K;i++)if(!vs[i])return 0;
return 1;
}
const int INF=0x3f3f3f3f;
queue<pair<int,int> >q;bool inq[235][235];
node check(int mid)
{
int i,j,k,s,t,x,y,_x,_y;node w;
for(i=1;i<=n;i++)for(j=1;j<=m;j++){
if(mp[i][j]<=mid)val[i][j]=-1;
else val[i][j]=1;
}
for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(col[i][j]!=-1){
for(s=1;s<(1<<K);s++)f[i][j][s]=node(INF,0);
f[i][j][1<<col[i][j]]=node(0,0);
}
for(s=1;s<(1<<K);s++){
for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(col[i][j]!=-1)
for(t=(s-1)&s;t;t=(t-1)&s)
f[i][j][s]=min(f[i][j][s],f[i][j][t]+f[i][j][s^t]);
for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(col[i][j]!=-1)
q.push(make_pair(i,j)),inq[i][j]=1;
while(!q.empty()){
x=q.front().first;y=q.front().second;q.pop();
inq[x][y]=0;// Attention!!!
for(k=0;k<=3;k++){
_x=x+dx[k];_y=y+dy[k];w=f[x][y][s]+node(1,val[x][y]);
if(col[_x][_y]!=-1&&w<f[_x][_y][s]){
f[_x][_y][s]=w;
if(!inq[_x][_y])q.push(make_pair(_x,_y)),inq[_x][_y]=1;
}
}
}
}
ans=node(INF,INF);
for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(col[i][j]!=-1)
ans=min(ans,f[i][j][(1<<K)-1]+node(1,val[i][j]));
return ans;
}
int main()
{
srand(0);
int T,i,j,l,r,mid;
T=gi();
while(T--){
n=gi();m=gi();K=gi();
for(i=1;i<=n;i++)for(j=1;j<=m;j++)a[i][j]=gi();
for(i=1;i<=n;i++)for(j=1;j<=m;j++)mp[i][j]=gi();
for(i=0;i<=n+1;i++)col[i][0]=col[i][m+1]=-1;
for(j=0;j<=m+1;j++)col[0][j]=col[n+1][j]=-1;
for(i=1;i<=n*m;i++)id[i]=i;
int T=150;
lans=node(-1,-1);
while(T--){
if(!build())continue;
l=0;r=1000000;
while(l<r){
mid=(l+r)>>1;
if(check(mid).y<=0) r=mid;
else l=mid+1;
}
node tp=check(l);tp.y=l;
if(lans.x==-1){if(tp.x!=INF)lans=tp;}
else lans=min(lans,tp);
}
printf("%d %d\n",lans.x,lans.y);
}
}