[THUSC2017]巧克力 (斯坦纳树+二分+随机化)

这是本菜鸡写过的最难的斯坦纳树题了。。。干脆叫它斯坦纳树终结者好了

 

 

 

 

题解

随机化的部分类似于这道题

只不过这里是要求中位数最小,那道题是要求和最小

我们可以二分这个中位数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);
	}
}

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章