[CodeJam2021]Square Free

题目

题意概要
把一个 n × m n\times m n×m 的方格图中填满 /\,规定第 i i i 行、第 i i i 列分别需要恰好有 r i r_i ri c i c_i ci/,并且不能形成正方形。

求任意一组方案。

数据范围与提示
n , m ⩽ 20 n,m\leqslant 20 n,m20

思路

首先,贪心地用较大的 r i r_i ri 填较大的 c i c_i ci,可以找到一组解。但是可能有正方形,怎么办呢?

考虑一个简单的方法,就是将正方形的左右 “尖角” 的状态翻转,如图

/...\   -->   \.../
\.../         /...\

你发现,这等价于左边的 / 下降而右边的 \ 上升。于是我们定义势能 Φ = ∑ ( i , j ) = ‘ / " i ( m − j ) \Phi=\sum_{(i,j)=`/"}i(m-j) Φ=(i,j)=/"i(mj),很容易发现势能必然减小。而势能最初是 O ( n 4 ) \mathcal O(n^4) O(n4) 的,所以最多进行这么多次调整。而且,有了这个势能,根本不需要判断是否存在正方形。

每次调整的时候,直接扫描相邻的两行,将所有这样的 “尖角” 翻转。这样一次是 O ( n 2 ) \mathcal O(n^2) O(n2) 的,复杂度就是 O ( n 6 ) \mathcal O(n^6) O(n6) 的。

还有更好的做法:直接求出 Φ \Phi Φ 最小的解。显然存在正方形时,则存在 Φ \Phi Φ 更小的解,所以 Φ \Phi Φ 最小时必然无正方形。直接用费用流,费用就是势能 i ( m − j ) i(m-j) i(mj) 呗。时间复杂度 O ( n 4 log ⁡ n ) \mathcal O(n^4\log n) O(n4logn)

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 22;
int n, m, a[MAXN][MAXN];
priority_queue< pair<int,int> > pq;
int r[MAXN], c[MAXN], id[MAXN];
bool cmp(const int &_x,const int &_y){
    
    
	return r[_x] < r[_y];
}
queue<int> bin;
int main(){
    
    
	n = readint(), m = readint();
	rep(i,1,n) r[id[i]=i] = readint();
	std::sort(id+1,id+n+1,cmp);
	rep(i,1,m){
    
    
		c[i] = readint();
		pq.push(make_pair(c[i],i));
	}
	drep(i,n,1){
    
    
		while(r[id[i]] --){
    
    
			if(pq.empty()) // nothing to choose
				return puts("IMPOSSIBLE"), 0;
			int v = pq.top().second; pq.pop();
			a[id[i]][v] = 1;
			if(-- c[v]) bin.push(v);
		}
		for(; !bin.empty(); bin.pop()){
    
    
			int x = bin.front();
			pq.push(make_pair(c[x],x));
		}
	}
	rep(j,1,m) if(c[j] != 0)
		return puts("IMPOSSIBLE"), 0;
	for(bool ok=false; !ok&&(ok=true); )
		rep(i,1,n-1) for(int j=1,k=m; j<k; ){
    
    
			while(j <= m && (a[i][j] == a[i+1][j] || !a[i][j])) ++ j;
			while(k && (a[i][k] == a[i+1][k] || a[i][k])) -- k;
			if(j < k){
    
    
				a[i+1][j] = a[i][k] = 1;
				a[i][j] = a[i+1][k] = 0, ok = false;
			}
		}
	puts("POSSIBLE");
	rep(i,1,n){
    
    
		rep(j,1,m)
			if(a[i][j]) putchar('/');
			else putchar('\\');
		putchar('\n');
	}
	return 0;
}

后记

没能构造出 O ( n 4 ) \mathcal O(n^4) O(n4) 次翻转,最多做到 O ( n 3 ) \mathcal O(n^3) O(n3),且最多跑 O ( n 2 ) \mathcal O(n^2) O(n2) 轮。无法证明。毕竟本来就是网络流,网络流的复杂度是玄学……

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/121380483