[学习笔记] THUSCH 2017 LOJ 2977 巧克力 - 斯坦纳树 - 随机化 - 学习笔记

题目大意:给你一张图,要求一个包含至少k种颜色的连通块使得在点数最少的前提下中位数最小。k<=5。
题解:首先中位数最小怎么办,可以二分答案,将小的视为M,大的视为M+1,其中M是个比较大的数字,然后要求权值之和最小。
然后可以说明一定存在一种最优解,包含恰好k种颜色(如果有解的话)。
那么我们给每种颜色随即一个[1,k]的权值,那么该最优解包含k种不同的权值概率大约会是 k ! k k \frac{k!}{k^k} kkk!,大约随机200次正确率就还可以了。
每次随机完了二分,求出权值和可以用斯坦纳树求,所有随机出来的答案取min即可。
斯坦纳树就是状态 d p ( x , S ) dp(x,S) dp(x,S)表示集合 S S S当前在 x x x,两种转移,先是合并集合 d p ( x , S ∣ T ) ← d p ( x , S ) × d p ( x , T ) − v a l u e ( x ) dp(x,S|T)\leftarrow dp(x,S)\times dp(x,T)-value(x) dp(x,ST)dp(x,S)×dp(x,T)value(x),然后用一个 s p f a spfa spfa求出第二种换根转移 d p ( y , S ) ← d p ( x , S ) + w e i g h t ( x , y ) dp(y,S)\leftarrow dp(x,S)+weight(x,y) dp(y,S)dp(x,S)+weight(x,y)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn() { int x;return scanf("%d",&x),x; }
const int INF=1000000,N=250,MXS=(1<<5)+5;const lint MXV=1000000000000;
int a[N][N],b[N][N],c[N][N],inq[N][N],id[N*N];lint v[N][N],dp[MXS][N][N];
queue<pii> q;int dx[5]={0,-1,0,1,0},dy[5]={0,0,1,0,-1},sz[MXS];
struct RAND{
	int x;const int p;RAND():p(998244353) { x=1; }
	inline int operator()(int a,int b) { return x=(0ll+x+x+x)%p,x%(b-a+1)+a; }
}rnd;
inline int spfa_trans(int n,int m,lint (*dp)[N])
{
	while(!q.empty()) q.pop();
	rep(i,1,n) rep(j,1,m) if(b[i][j]>=0)
		q.push(mp(i,j)),inq[i][j]=1;
	while(!q.empty())
	{
		pii p=q.front();int x=p.fir,y=p.sec;
		q.pop();inq[x][y]=0;
		rep(i,1,4)
		{
			int px=x+dx[i],py=y+dy[i];
			if(px>0&&px<=n&&py>0&&py<=m&&dp[px][py]>dp[x][y]+v[px][py])
			{
				dp[px][py]=dp[x][y]+v[px][py];
				if(!inq[px][py]) inq[px][py]=1,q.push(mp(px,py));
			}
		}
	}
	return 0;
}
inline lint calc(int n,int m,int k,int bas)
{
	rep(i,1,n) rep(j,1,m)
		if(b[i][j]>=0) v[i][j]=INF+(a[i][j]>bas);
		else v[i][j]=MXV;
	int all=(1<<k)-1;
	rep(i,1,n) rep(j,1,m) rep(t,0,k-1)
		if(t==b[i][j]) dp[1<<t][i][j]=v[i][j];
		else dp[1<<t][i][j]=MXV;
	rep(s,1,all)
	{
		sz[s]=sz[s>>1]+(s&1);
		if(sz[s]>1)
		{
			rep(i,1,n) rep(j,1,m) dp[s][i][j]=MXV;
			for(int t=(s-1)&s;t;t=(t-1)&s) rep(i,1,n) rep(j,1,m)
				dp[s][i][j]=min(dp[s][i][j],dp[t][i][j]+dp[s^t][i][j]-v[i][j]);
		}
		spfa_trans(n,m,dp[s]);
	}
	lint ans=MXV;
	rep(i,1,n) rep(j,1,m) ans=min(ans,dp[all][i][j]);
	return ans;
}
inline pii solve(int n,int m,int k,int _R)
{
	int L=0,R=_R;rep(i,1,n*m) id[i]=-1;
	rep(i,1,n) rep(j,1,m)
	{
		if(c[i][j]==-1) { b[i][j]=-1;continue; }
		if(id[c[i][j]]<0) id[c[i][j]]=rnd(0,k-1);
		b[i][j]=id[c[i][j]];
	}
	if(calc(n,m,k,R)==MXV) return mp(n*m+1,R);
	while(L<=R)
	{
		int mid=(L+R)>>1;lint t=calc(n,m,k,mid);
		int cnt=int(t/INF),sml=cnt-(int)(t%INF);
		if(sml>=(cnt+1)/2) R=mid-1;else L=mid+1;
	}
	lint t=calc(n,m,k,L);return mp(int(t/INF),L);
}
int main()
{
	for(int T=inn();T;T--)
	{
		int n=inn(),m=inn(),k=inn(),colcnt=0,R=0;
		rep(i,1,n) rep(j,1,m) c[i][j]=inn();
		rep(i,1,n) rep(j,1,m) a[i][j]=inn();
		rep(i,1,n) rep(j,1,m) R=max(R,a[i][j]);
		pii ans=mp(n*m+1,R);
		rep(Tms,1,200) ans=min(ans,solve(n,m,k,R));
		if(ans.fir>n*m) printf("-1 -1\n");
		else printf("%d %d\n",ans.fir,ans.sec);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值