[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);
	}
}

 

 

 

 

 

 

 

 

 

 

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