题目描述
“人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道。”
明明收到了一大块巧克力,里面有若干小块,排成n行m列。每一小块都有自己特别的图案ci,j,它们有的是海星,有的是贝壳,有的是海螺......其中还有一些因为挤压,已经分辨不出是什么图案了。明明给每一小块巧克力标上了一个美味值ai,j ( 0≤ai,j≤106 ),这个值越大,表示这一小块巧克力越美味。
正当明明咽了咽口水,准备享用美味时,舟舟神奇地出现了。看到舟舟恳求的目光,明明决定从中选出一些小块与舟舟一同分享。
舟舟希望这些被选出的巧克力是连通的(两块巧克力连通当且仅当他们有公共边),而且这些巧克力要包含至少k ( 1≤k≤5 )种。而那些被挤压过的巧克力则是不能被选中的。
明明想满足舟舟的愿望,但他又有点“抠”,想将美味尽可能多地留给自己。所以明明希望选出的巧克力块数能够尽可能地少。如果在选出的块数最少的前提下,美味值的中位数(我们定义n个数的中位数为第⌊n+12⌋小的数)能够达到最小就更好了。
你能帮帮明明吗?
输入格式
从标准输入读入数据。
每个测试点包含多组测试数据。
输入第一行包含一个正整数 T (1≤T≤5),表示测试数据组数。
对于每组测试数据:
输入第一行包含三个正整数n,m和k;
接下来n行,每行m个整数,表示每小块的图案ci,j。若ci,j=−1表示这一小块受到过挤压,不能被选中;
接下来n行,每行m个整数,表示每个小块的美味值ai,j。
输出格式
输出到标准输出。
输出共包括T行,每行包含两个整数,用空格隔开,即最少的块数和最小的美味值中位数。
若对于某组测试数据,不存在任意一种合法的选取方案,请在对应行输出两个−1。
数据范围 n×m≤233 ci,j=−1或1≤ci,j≤n×m
题解:
-
- 二分一个出现过的美味度做中位数,考虑如何$check$;
- 而判断一个数$x$>=中位数,只要在数列里面找到至少$\lfloor \frac{n+1}{2} \rfloor$个小于等于$x$的数即可;
- 将$a_{ij}$大于中位树的设为$1001$,小于等于的设为$999$
- 在满足连通和颜色的条件下取尽量小的代价和就做到了个数做第一关键字,中位数做第二关键字;
- 所以每次将(最小值+500)/1000得到个数,将最小值和个数*1000比较,如果小于等于证明合法;
- 具体地求最小值:
- 将颜色在$0-k$内随机一个新颜色,考虑新颜色$0-k$全都取就取了$k$种,多随机几次;
- 每次斯坦纳树求出最小代价;
- $spfa$算满的话是:$O(T* t *(nm \ 3^{k} + (nm)^2 \ 2^k) )$;
- 感觉整个题充满了脑洞。。。。
-
(最开始写的解法里面,等于的设置成1000,在最小个数为偶数的情况下会wa,希望没有坑到太多人。。。。。)
-
1 #include<bits/stdc++.h> 2 #define inf 0x3f3f3f3f 3 #define fir first 4 #define sec second 5 #define mk make_pair 6 using namespace std; 7 const int N=250; 8 int n,m,k,a[N][N],c[N][N],sub[N],tot,f[N][N][1<<5],id[N][N],col[N],b[N][N],ans1,ans2,vis[N][N]; 9 typedef pair<int,int>pii; 10 queue<pii>q; 11 int dx[4]={0,0,1,-1}; 12 int dy[4]={1,-1,0,0}; 13 void spfa(int s){ 14 while(!q.empty()){ 15 int x=q.front().fir, y=q.front().sec; 16 q.pop();vis[x][y]=0; 17 for(int i=0;i<4;i++){ 18 int nx=x+dx[i], ny=y+dy[i]; 19 if(nx<1||nx>n||ny<1||ny>m||!~c[nx][ny])continue; 20 if(f[nx][ny][s] > f[x][y][s] + b[nx][ny]){ 21 f[nx][ny][s] = f[x][y][s] + b[nx][ny]; 22 if(!vis[nx][ny])vis[nx][ny]=1,q.push(mk(nx,ny)); 23 } 24 } 25 } 26 } 27 void steiner(){ 28 for(int i=1;i<=n;i++) 29 for(int j=1;j<=m;j++){ 30 for(int s=0;s<1<<k;s++)f[i][j][s]=inf; 31 if(~c[i][j])f[i][j][1<<col[c[i][j]]]=b[i][j]; 32 } 33 for(int s=1;s<1<<k;s++){ 34 for(int i=1;i<=n;i++) 35 for(int j=1;j<=m;j++)if(~c[i][j]){ 36 for(int t=s&(s-1);t;t=s&(t-1)){ 37 f[i][j][s] = min(f[i][j][s], f[i][j][t] + f[i][j][s^t] - b[i][j]); 38 } 39 if(f[i][j][s]!=inf)q.push(mk(i,j)),vis[i][j]=1; 40 } 41 spfa(s); 42 } 43 for(int i=1;i<=n;i++) 44 for(int j=1;j<=m;j++)ans1 = min(ans1, f[i][j][(1<<k)-1]); 45 } 46 int main(){ 47 freopen("T1.in","r",stdin); 48 freopen("T1.out","w",stdout); 49 srand(time(NULL)); 50 int T;scanf("%d",&T); 51 while(T--){ 52 scanf("%d%d%d",&n,&m,&k); 53 tot=0; 54 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&c[i][j]); 55 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]),sub[id[i][j]=++tot]=a[i][j]; 56 sort(sub+1,sub+tot+1); 57 tot = unique(sub+1,sub+tot+1) - sub - 1; 58 int l=1,r=tot,fg=0; 59 while(l<r){ 60 int mid=(l+r)>>1; 61 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)b[i][j]=a[i][j]>sub[mid]?1001:/*a[i][j]<sub[mid]?*/999/*:1000*/; 62 ans1 = inf; 63 for(int I=1;I<=400;I++){ 64 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)col[id[i][j]]=rand()%k; 65 steiner(); 66 } 67 if(ans1==inf){fg=1;puts("-1 -1");break;} 68 ans2 = (ans1+500) / 1000; 69 if(ans1 <= ans2 * 1000)r=mid; 70 else l=mid+1; 71 } 72 if(!fg)printf("%d %d\n",ans2,sub[l]); 73 } 74 return 0; 75 }