#C241025B. [THUSCH2017] 巧克力


#C241025B. [THUSCH2017] 巧克力

标签(Label)

  • 斯坦纳树
  • 动态规划
  • 状态压缩
  • Color Coding

网址(Website)

P7450 [THUSCH2017] 巧克力 - 洛谷

题目(Problem)

题目描述

「人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道。」

明明收到了一大块巧克力,里面有若干小块,排成 $n$ 行 $m$ 列。每一小块都有自己特别的图案 ,它们有的是海星,有的是贝壳,有的是海螺……其中还有一些因为挤压,已经分辨不出是什么图案了。明明给每一小块巧克力标上了一个美味值 $a_{i,j}$($0\le a_{i,j}\le 10^6$),这个值越大,表示这一小块巧克力越美味。

正当明明咽了咽口水,准备享用美味时,舟舟神奇地出现了。看到舟舟恳求的目光,明明决定从中选出一些小块与舟舟一同分享。

舟舟希望这些被选出的巧克力是连通的(两块巧克力连通当且仅当他们有公共边),而且这些巧克力要包含至少 $k$($1\le k\le 5$)种。而那些被挤压过的巧克力则是不能被选中的。

明明想满足舟舟的愿望,但他又有点「抠」,想将美味尽可能多地留给自己。所以明明希望选出的巧克力块数能够尽可能地少。如果在选出的块数最少的前提下,美味值的中位数(我们定义 $n$ 个数的中位数为第 $\left\lfloor\frac{n+1}{2}\right\rfloor$ 小的数)能够达到最小就更好了。

你能帮帮明明吗?

输入格式

每个测试点包含多组测试数据。

输入第一行包含一个正整数 $T$($1\le T\le 5$),表示测试数据组数。

对于每组测试数据:

输入第一行包含三个正整数 $n,m$ 和 $k$;

接下来 $n$ 行,每行 $m$ 个整数,表示每小块的图案 $c_{i,j}$。若 $c_{i,j}=-1$ 表示这一小块受到过挤压,不能被选中;

接下来 $n$ 行,每行 $m$ 个整数,表示每个小块的美味值 $a_{i,j}$。

输出格式

输出共包括 $T$ 行,每行包含两个整数,用空格隔开,即最少的块数和最小的美味值中位数。

若对于某组测试数据,不存在任意一种合法的选取方案,请在对应行输出两个 $-1$。

样例 #1

样例输入 #1

1
5 4 5
3 4 3 4
5 5 -1 5
-1 4 5 5
5 5 4 2
1 -1 2 4
1 3 1 1
3 2 3 3
4 4 4 5
8 9 9 5
7 2 6 3

样例输出 #1

9 5

提示

测试点编号$n,m$ 的限制$c_{i,j}$ 的限制部分分说明
1$n=1,1\le m\le233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$$\text{A}$
2$1\le n\times m\le 20$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$$\text{A}$
3~4$n=2,m=15$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$$\text{A}$
5~6$1\le n\times m\le 30$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$$\text{A}$
7~9$1\le n\times m\le 50$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le8$$\text{A}$
10$1\le n\times m\le 233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le8$$\text{A}$
11~12$1\le n\times m\le 233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le8$$\text{B}$
13~15$1\le n\times m\le 233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le14$$\text{B}$
16~20$1\le n\times m\le 233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$$\text{B}$
21$1\le n\times m\le 233$$c_{i,j}=-1$ 或 $1\le c_{i,j}\le n\times m$该测试点不计分。

$\text{A}$:若输出的最少块数均正确,但最小中位数存在错误,选手可以获得该测试点 $80%$ 的分数。
$\text{B}$:若输出的最少块数均正确,但最小中位数存在错误,选手可以获得该测试点 $60%$ 的分数。

题解(Solution)

$\qquad$感觉理解的不太清晰。。。

P7450 [THUSCH2017] 巧克力 - 洛谷

代码(Code)

#include<bits/stdc++.h>
#include<vector>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int>
#define x first
#define y second
#define in(x,l,r) (l<=x && x<=r)
inline int rd(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}const int inf = 0x3f3f3f3f;
const int S = 1<<10;
const int N = 350;
bool ST;
mt19937 rr(time(0));inline int rnd(int l,int r){return rr()%(r-l+1)+l;}

int a[N][N], c[N][N], w[N][N];
int n,m,k;
int f[N][N][S];

int dx[] = {0,0,-1,1};
int dy[] = {-1,1,0,0};
bitset<N> vst[N];
queue<P> q;
void spfa(int s){
	while(q.size()){
		int x,y;tie(x,y)=q.front();q.pop();
		vst[x][y] = false;
		For(k,0,3){
			int i = x+dx[k], j = y+dy[k];
			if(in(i,1,n) && in(j,1,m) && ~c[i][j]){
				if(f[x][y][s] + w[i][j] < f[i][j][s]){
					f[i][j][s] = f[x][y][s] + w[i][j];
					if(!vst[i][j])q.push({i,j}),vst[i][j]=true;
				}
			}
		}
	}
}

int COL[N*10],to[N],tot;
auto col=COL+114514;
int calc(){
	int res = inf;
	For(_,1,200){
//		shuffle(col + 1, col + tot + 1, rr);
		For(i, 1, tot) to[col[i]] = rnd(0,k-1);
		
		For(i,1,n) For(j,1,m){
			For(s, 0, (1 << k) - 1) f[i][j][s] = inf;
			if(c[i][j]!=-1) f[i][j][1<<to[c[i][j]]] = w[i][j];
		}
		For(s,1,(1<<k)-1){
			For(i,1,n) For(j,1,m) if(~c[i][j]){
				for(int t = s & (s-1); t; t = (t-1) & s){
					f[i][j][s] = min(f[i][j][s], f[i][j][t]+f[i][j][s^t]-w[i][j]);
				}if(f[i][j][s] <= inf) q.push({i,j}),vst[i][j]=true;
			}spfa(s);
		}For(i,1,n) For(j,1,m) res = min(res, f[i][j][(1<<k)-1]); 
	}return res;
}

void Solve(){
	n = rd(), m = rd(), k = rd();
	For(i,1,n) For(j,1,m) c[i][j] = col[++tot] = rd();
	For(i,1,n) For(j,1,m) a[i][j] = rd(), w[i][j] = 1;
	sort(col+1,col+tot+1),tot = unique(col+1,col+tot+1)-col-1;
	
	int res = calc();
	if(res == inf) return printf("-1 -1\n"),void();
	int l = 0, r = 1e6, mid;
	while(l<r){
		mid = (l+r)>>1;
		For(i,1,n) For(j,1,m) w[i][j] = a[i][j]<=mid ? 9999 : 10001;
		int now = calc();
		if(now <= res * 10000) r = mid;
		else l = mid+1;
	}printf("%d %d\n", res, l);
}

bool ED;
signed main(){
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	int T = rd();double Tim = clock();
	while(T--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录