LOJ 2977 「THUSCH 2017」巧克力

LOJ 2977 「THUSCH 2017」巧克力

神仙题QaQ

做法是给每种颜色随机分配一个 $ 1 $ 到 $ k $ 的颜色,然后跑一次斯坦纳树,得到当前包含至少 $ k $ 种颜色的最小联通块。

我们考虑什么时候我们可以随机到答案?如果答案的 $ k $ 颜色恰好本随机分配到了不同的颜色,那么跑出来的答案肯定就是正确的。所以说,一次随机的正确率大概是 $ \frac{k!}{k^k} $ 大概是 $ 0.3% $ 。所以我们随机大概 200 次,正确率就远超 100 了。

另外考虑中位数尽量小这个限制,我们二分这个中位数,考虑怎么 check ,当前我们的第一限制是块数,第二限制是拿到的大于中位数的尽可能少。由于我们要让 $ k $ 种联通,最多要选的块也不会超过 $ 5 \times (233 + 233) $ 不会超过 2000 多,实际上还肯定比这个小得多,保险起见(其实是方便)直接把大于中位数设置为 $ 10001 $ ,小于中位数 $ 9999 $ 。然后最后块数就是 $ \lfloor \frac{ans + 2500}{10000} \rfloor $ ,这样就可以方便的验证了。

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "vector"
#include "queue"
using namespace std;
int dirx[4] = { 0,0,-1,1 };
int diry[4] = { 1,-1,0,0 };
#define chkmn( a , b ) ( (a) > (b) ? ( (a) = (b) , 1 ) : 0 )
#define MAXN 256
int n , m , k , s;
int c[MAXN][MAXN] , A[MAXN][MAXN] , w[MAXN][MAXN];
int col[MAXN] , en , rc[MAXN];
int dp[MAXN][MAXN][1<<5];
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
queue<pii> Q;
int vis[MAXN][MAXN];
void spfa( int s ) {
    while( !Q.empty() ) {
        int x = Q.front().fi , y = Q.front().se; Q.pop();
        vis[x][y] = 0;
        for( int d = 0 ; d < 4 ; ++ d ) {
            int X = x + dirx[d] , Y = y + diry[d];
            if( X < 1 || X > n || Y < 1 || Y > m || c[X][Y] == -1 ) continue;
            if( chkmn( dp[X][Y][s] , dp[x][y][s] + w[X][Y] ) && !vis[X][Y] )
                Q.push( mp( X , Y ) ) , vis[X][Y] = 1;
        }
    }
}
int work(  ) {
    int ans = 0x3f3f3f3f;
    for( int t = 1 ; t <= 200 ; ++ t ) {
        random_shuffle( col + 1 , col + 1 + s );
        for( int i = 1 ; i <= s ; ++ i ) rc[col[i]] = i % k;
        for( int i = 1 ; i <= n ; ++ i )
            for( int j = 1 ; j <= m ; ++ j ) {
                for (int st = 0; st < (1 << k); ++st) dp[i][j][st] = 0x3f3f3f3f;
                if( ~c[i][j] ) dp[i][j][1<<rc[c[i][j]]] = w[i][j];
            }
        for( int st = 1 ; st < ( 1 << k ) ; ++ st ) {
            for( int i = 1 ; i <= n ; ++ i )
                for( int j = 1 ; j <= m ; ++ j ) {
                    for( int t = ( st - 1 ) & st ; t ; t = ( t - 1 ) & st )
                        chkmn( dp[i][j][st] , dp[i][j][t] + dp[i][j][st ^ t] - w[i][j] ); // 转移
                    if( dp[i][j][st] < 1e9 ) Q.push( mp( i , j ) );
                }
            spfa( st );
        }
        for( int i = 1 ; i <= n ; ++ i )
            for( int j = 1 ; j <= m ; ++ j ) ans = min( ans , dp[i][j][( 1 << k ) - 1]);
    }
    return ans;
}
int res;
int main() {
    srand( 19260817 );
    int T;cin >> T;
    while( T --> 0 ) {
        cin >> n >> m >> k;
        for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
            scanf("%d",&c[i][j]) , col[++ en] = c[i][j];
        for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
            scanf("%d",&A[i][j]);
        sort( col + 1 , col + 1 + en );
        s = unique( col + 1 , col + 1 + en ) - col - 1;
        for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j ) w[i][j] = 1;
        res = work( );
        if( res > 1e9 ) { puts("-1 -1"); continue; }
        int l = 0 , r = 1e6;
        while( l <= r ) {
            int mid = l + r >> 1;
            for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
                w[i][j] = ( A[i][j] <= mid ? 9999 : 10001 );
            int re = work( );
            if( re <= ( re + 2500 ) / 10000 * 10000 ) r = mid - 1;
            else l = mid + 1;
        }
        printf("%d %d\n",res,l);
    }
}
posted @ 2020-02-16 22:01  yijan  阅读(243)  评论(0编辑  收藏  举报