题解 P3936 【Coloring】

xzyxzy

2018-05-11 12:44:58

Solution

### 模拟退火 #### 这里有一个搜索专讲的[课件](http://www.cnblogs.com/xzyxzy/p/8546384.html)?(预计2018暑假之前完成吧,大家可以看一看,也许不是以课件的形式呈现) 思路就是rand一个答案,然后退火地交换,直到最优 计算贡献用另外一种方便点的方式:每个点往四周算,那么方格内的贡献被算两次,边界上被算一次,但是不影响 ## Code ```cpp // luogu-judger-enable-o2 // luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> using namespace std; int N,M,C,p[51]; int A[25][25],E[51],out[25][25]; int sum,ans=1000000; int Get(int x,int y) { int Ans=0; if(A[x][y]!=A[x-1][y]) Ans++; if(A[x][y]!=A[x+1][y]) Ans++; if(A[x][y]!=A[x][y-1]) Ans++; if(A[x][y]!=A[x][y+1]) Ans++; return Ans; } void Update(int k) { if(ans<=k) return; ans=k; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) out[i][j]=A[i][j]; } double Rand(){return 1.0*(rand()%100000000)/100000000;} int Rand(int x){return rand()%x+1;} void SA(double T) { while(T>1e-15) { T*=0.99999; int x1=Rand(N),x2=Rand(N); int y1=Rand(M),y2=Rand(M); int p=sum; p-=Get(x1,y1)+Get(x2,y2); swap(A[x1][y1],A[x2][y2]); p+=Get(x1,y1)+Get(x2,y2); if(p<sum||exp((sum-p)/T)>Rand()) {Update(sum=p);continue;} swap(A[x1][y1],A[x2][y2]); } } int main() { srand(19260817233); cin>>N>>M>>C; for(int i=1;i<=C;i++) cin>>p[i]; int T=5; while(T--) { for(int i=1;i<=C;i++) E[i]=p[i]; for(int i=1;i<=N;i++) for(int j=1,x=1;j<=M;j++) { x=rand()%C+1; while(!E[x]) x=rand()%C+1; A[i][j]=x; E[x]--; } sum=0; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) sum+=Get(i,j); Update(sum);SA(100000000); } for(int i=1;i<=N;i++,puts("")) for(int j=1;j<=M;j++) printf("%d ",out[i][j]); return 0; } //之前这里一个错误找了一周:定义了一个B存该点贡献,但是交换的话应该改动10个B //而我只改了2个,把贡献算成负数,但是somehowAC了 ``` ## 不保证程序能一遍AC(你也看到了是完全随机的随机种子) ~~另外我很迷的地方是这个now(贡献)算到最后是负数。。~~ ~~有没有大佬能教教我QAQ~~ 好的找到原因了Upd:7.18