• * ! THUSCH2017巧克力



    随机化算法!!!

    中位数可以二分,把(a_{i,j}leq mid)赋为-1,否则为1,若DP最小值后(leq0)则可以继续往更小的地方DP

    假设我们已经知道答案了,(kleq5)又要最小代价联通,斯坦纳树!

    我们可以把每个颜色随机放入(k)个盒子,每个盒子选一个,那么至少有(k)种颜色

    每次成功的概率为(frac{k!k^{col-k}}{k^{col}}=frac{k!}{k^k}),col为总颜色数

    次成功概率为(1-(1-frac{k!}{k^k})^T),200次后就非常高了

    步骤:

    1. 随机
    2. 二分
    3. 检查(两个DP数组,第一个为个数(先保证个数,每次DP一定不变),第二个为关于中位数的那个)
    #include<bits/stdc++.h>
    using namespace std;
    mt19937 gen(chrono::steady_clock().now().time_since_epoch().count());
    #define rand(r) (uniform_int_distribution<int>(1, r)(gen))
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f==1?x:-x;
    }
    const int N=240,inf=0x3f3f3f3f;
    int n,m,k,cn,num,mn,mk,ans1,ans2;
    int b[N],bel[N],vis[N],a[N][N],c[N][N],d[N][N],f[N][40],g[N][40];
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    queue<int>q;
    inline bool upt(int x,int s,int ff,int gg){
    	if(ff<f[x][s]||(ff==f[x][s]&&gg<g[x][s])){
    		f[x][s]=ff;g[x][s]=gg;
    		return 1;
    	}
    	return 0;
    }
    inline int id(int i,int j){
    	return (i-1)*m+j;
    }
    inline bool check(int mid){
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			d[i][j]=(a[i][j]<=mid)?-1:1;
    	memset(g,0x3f,sizeof(g));
    	memset(f,0x3f,sizeof(f));
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)if(c[i][j]!=-1){
    			f[id(i,j)][1<<bel[c[i][j]]-1]=1;
    			g[id(i,j)][1<<bel[c[i][j]]-1]=d[i][j];
    		}
    	for(int s=1,S=(1<<k),x,y,u,v,nx,ny;s<S;s++){
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++){
    				x=id(i,j);
    				for(int sta=(s-1)&s;sta;sta=(sta-1)&s)
    					upt(x,s,f[x][sta]+f[x][s^sta]-1,g[x][sta]+g[x][s^sta]-d[i][j]);
    				if(f[x][s]!=inf){q.push(x);vis[x]=1;}
    			}
    		while(!q.empty()){
    			u=q.front();q.pop();
    			vis[u]=0;
    			y=u%m;if(!y)y=m;
    			x=(u-y)/m+1;
    			for(int i=0;i<4;i++){
    				nx=x+dx[i];ny=y+dy[i];
    				v=id(nx,ny);
    				if(nx<1||nx>n||ny<1||ny>m||c[nx][ny]==-1)continue;
    				if(upt(v,s,f[u][s]+1,g[u][s]+d[nx][ny])&&!vis[v]){q.push(v);vis[v]=1;}
    			}
    		}
    	}
    	num=mn=inf;
    	for(int i=1,s=(1<<k)-1,x;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			x=id(i,j);
    			if(f[x][s]<num||(f[x][s]==num&&g[x][s]<mn)){
    				num=f[x][s];mn=g[x][s];
    			}
    		}
    	return mn<=0;
    }
    inline void stn(){
    	for(int i=1;i<=mk;i++)bel[i]=rand(k);
    	static int l,r,mid;
    	l=1;r=cn;
    	while(l<r){
    		mid=l+r>>1;
    		if(check(mid))r=mid;
    		else l=mid+1;
    	}
    	if(num<ans1||(ans1==num&&r<ans2)){ans1=num;ans2=r;}
    }
    inline void solve(){
    	n=read();m=read();k=read();
    	mk=0;ans1=ans2=inf;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){c[i][j]=read();mk=max(mk,c[i][j]);}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)b[id(i,j)]=a[i][j]=read();
    	sort(b+1,b+n*m+1);
    	cn=unique(b+1,b+n*m+1)-b-1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			a[i][j]=lower_bound(b+1,b+cn+1,a[i][j])-b;
    	for(int T=150;T;T--)stn();
    	cout<<ans1<<" "<<b[ans2]<<"
    ";
    }
    int main(){
    	int T=read();
    	while(T--)solve(); 
    	return (0-0);
    }
    
  • 相关阅读:
    博客园装修 js动态背景效果 一个随鼠标变换的动态线条
    转载:jQuery在线引用地址
    记录下es6的基础笔记
    js、css 、html平时的一些小笔记
    【机器学*】k*邻算法-01
    【Leetcode】718. 最长重复子数组
    【嵌入式】嵌入式系统开发与应用第二版课后答案第三章(田泽)
    【嵌入式】嵌入式系统开发与应用第二版课后答案第二章(田泽)
    【嵌入式】嵌入式系统开发与应用第二版课后答案第一章(田泽)
    【C++】关键字回忆leetcode题解
  • 原文地址:https://www.cnblogs.com/aurora2004/p/12762646.html
Copyright © 2020-2023  润新知