题目描述
在一个 n × m n\times m n×m 的矩阵中,每个点都染了一种颜色(只能是 [ 1 , c ] [1,c] [1,c] 中的一种),求一种方案,使得相邻异色点对数最小。
Solution 3936 \text{Solution 3936} Solution 3936
这是一道好题。正所谓代码五分钟,调参两百年。
随机一个矩阵作为初始矩阵。每次降温时尝试交换两个随机元素,判断交换后是否更优。详见代码。
对于每次交换,更新 c a l c calc calc 函数可以在 O ( 1 ) O(1) O(1) 中完成(提示:一个元素仅能与其上下左右相邻的元素产生贡献,仅更新交换的两个元素即可)。这部分请读者自行完成推导。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#define reg register
int n,m,c;
int p[60];
struct node{
int a[30][30];
}o,ans;
int answ=0x3f3f3f3f;
int calc(node a){
int cnt=0;
for(reg int i=1;i<=n;++i)
for(reg int j=1;j<=m;++j){
if(i>=2&&a.a[i][j]!=a.a[i-1][j]) ++cnt;
if(i<=n-1&&a.a[i][j]!=a.a[i+1][j]) ++cnt;
if(j>=2&&a.a[i][j]!=a.a[i][j-1]) ++cnt;
if(j<=n-1&&a.a[i][j]!=a.a[i][j+1]) ++cnt;
}
return cnt/2;
}
void SA(){
double t=1.0;
while(t>1e-15){
node no=ans;
int x1,y1,x2,y2,tt;
do{
x1=rand()%n+1;
y1=rand()%m+1;
x2=rand()%n+1;
y2=rand()%m+1;
}while(x1==x2&&y1==y2);
tt=no.a[x1][y1];no.a[x1][y1]=no.a[x2][y2];no.a[x2][y2]=tt;
int nw=calc(no);
int delta=nw-answ;
if(delta<0){
answ=nw;
ans=o=no;
}
else if(exp(-delta/t)*32767>rand()) o=no;
t*=0.9999;
}
}
void work(){
int now=1,pt=1,xx=1,yy=1; //随便搞一个矩阵作为初始矩阵
for(reg int i=1;i<=n*m;++i){
o.a[xx][yy]=now;
if(yy==m) yy=0,++xx;
if(pt>=p[now]){
pt=0;
++now;
}
++yy;++pt;
}
o.a[n][m]=c;ans=o;
for(reg int i=1;i<=10;++i) SA();
}
int main(){
srand(1007);
scanf("%d%d%d",&n,&m,&c);
for(reg int i=1;i<=c;++i)
scanf("%d",&p[i]);
work();
for(reg int i=1;i<=n;++i){
for(reg int j=1;j<=m;++j)
printf("%d ",ans.a[i][j]);
puts("");
}
// printf("%d",calc(ans));
}