這是本菜雞寫過的最難的斯坦納樹題了。。。乾脆叫它斯坦納樹終結者好了
題解
隨機化的部分類似於這道題
只不過這裏是要求中位數最小,那道題是要求和最小
我們可以二分這個中位數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);
}
}