Topcoder 13503 ConnectingGame
Posted Neal_lee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Topcoder 13503 ConnectingGame相关的知识,希望对你有一定的参考价值。
题目链接
Vjudge ConnectingGame Topcoder 13503 ConnectingGame
题目大意
有一个 \\(n\\times m\\) 的字符矩阵,字符相同的地方组成一个区块,所有区块的都是四联通的,问是否存在方案,把每个区块染成红色或蓝色,使得不存在从上到下的一条蓝色的通路,同时不存在从左到右的一条红色的通路。
\\(1\\leq n,m\\leq 30\\)
思路
从找规律和看样例着手,观察这一组样例:
SSnukee
SKnuthe
SSSothe
画出来长这样:
一组不存在通路的方案是这样的:
多画几组样例,能够发现把通路断开来的关键的地方都是如上图 “十字路口” 的地方,使得对角是相同颜色,相邻的是不同的颜色,把两条路同时断开了,于是猜结论:
图中存在 "十字路口" 是存在方案的必要条件
证明的话是一个简单的数学组合问题
(征求严谨证明中)
接下来考虑十字会如何产生一组解,我们沿着十字所划开来的边界走,直到走到矩阵的边缘,可以发现,一旦走到边缘,其实就相当于把整张图切成了四半,然后每一半内直接涂同一种颜色就是一组解了。
这个问题可以抽象到无向图上,有一张 \\((n+1)(m+1)\\) 节点的无向图,有一个起点和若干终点,要求找到四条路径,分别从起点连到一个终点,并且路径两两不重合。
仔细想想会发现这是一个网络流问题,给所有终点建一个汇点连有向边过去,所有边的流量设为 \\(1\\),然后检查最大流是否 \\(\\geq 4\\) 即可。不过呢,在这道题目中,并不是随便找四条分隔线就可以的,有如下几种特殊情况:
总结下来,线的分布要满足一些条件,
- 矩阵的一条边上的线数量 \\(\\leq 3\\)
- 任意两条边的线的数量 \\(\\leq 4\\)
任意两条边的线的数量 \\(\\leq 4\\) ,这就不是简单的求和关系了,似乎不好直接建图,但是每个边单独枚举流量这样效率十分低下。在这个地方我想了一段时间,找到了一个有趣的解决办法,
(无手写板人谢罪)如上图,先把每一条边界上的点连有向边指向对应的红点,相邻的两个红点再连边到对应的蓝点,蓝点再汇集到汇点处,这里所有边的容量全部为 \\(1\\),画一下,可以发现刚好满足了上面的限制!
有了这样的解决方案,我们直接根据连通块分界线的关系建图,然后以 ”十字路口“ 为源点跑 \\(Dinic\\) 最大流即可,时间复杂度为 \\(O(n^6)\\),不过众所周知这个上界远远达不到,实际表现总耗时 \\(604ms\\),非常快。
Code
#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#define mem(a,b) memset(a, b, sizeof(a))
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 33
#define Inf 0x3f3f3f3f
using namespace std;
class ConnectingGame{
public:
int head[N*N], to[2*N*N], nxt[2*N*N];
int origin[2*N*N], deg[N*N];
int cap[2*N*N], dis[N*N], now[2*N*N];
int cnt, T;
queue<int> q;
int n, m;
vector<string> s;
void init(){ mem(head, -1), cnt = -1; }
void add_e(int a, int b, int id, int w){
nxt[++cnt] = head[a], head[a] = cnt, to[cnt] = b;
origin[cnt] = w, deg[a] += (w != 0);
if(id == 2) add_e(b, a, 0, w);
if(id == 1) add_e(b, a, 0, 0);
}
int f(int x, int y){ return x*(m+1)+y; }
void build(){
init();
rep(i,0,n-1) rep(j,0,m-2){
if(s[i][j] != s[i][j+1]) add_e(f(i,j+1), f(i+1,j+1), 2, 1);
}
rep(i,0,n-2) rep(j,0,m-1){
if(s[i][j] != s[i+1][j]) add_e(f(i+1,j), f(i+1,j+1), 2, 1);
}
int st = (n+1)*(m+1);
rep(i,0,m) add_e(f(0,i), st+0, 1, 1), add_e(f(n,i), st+2, 1, 1);
rep(i,0,n) add_e(f(i,0), st+1, 1, 1), add_e(f(i,m), st+3, 1, 1);
rep(i,0,3) add_e(st+i, st+4+i, 1, 1), add_e(st+(i+1)%4, st+4+i, 1, 1);
T = (n+1)*(m+1)+8;
rep(i,0,3) add_e(st+4+i, T, 1, 1);
}
bool bfs(int S){
mem(dis, 0), dis[S] = 1;
q.push(S);
while(!q.empty()){
int cur = q.front(); q.pop();
now[cur] = head[cur];
for(int i = head[cur]; ~i; i = nxt[i]){
if(cap[i] && !dis[to[i]])
dis[to[i]] = dis[cur]+1, q.push(to[i]);
}
}
return dis[T];
}
int dfs(int x, int flow){
if(x == T) return flow;
int flown = 0;
for(int i = now[x], y; ~i; i = nxt[i]){
now[x] = i;
if(cap[i] && dis[(y = to[i])] == dis[x]+1){
int t = dfs(y, min(flow-flown, cap[i]));
cap[i] -= t, cap[i^1] += t;
flown += t;
if(flown == flow) break;
}
}
return flown;
}
string isValid(vector<string> board){
s = board;
n = s.size(), m = s[0].size();
build();
rep(i,0,(n+1)*(m+1)-1) if(deg[i] == 4){
rep(e,0,cnt) cap[e] = origin[e];
int flown = 0;
while(bfs(i)) flown += dfs(i, Inf);
if(flown >= 4) return "invalid";
}
return "valid";
}
} solve;
int main(){
int n;
string s;
vector<string> read;
cin>>n;
rep(i,1,n) cin>>s, read.push_back(s);
cout<< solve.isValid(read) <<endl;
return 0;
}
延伸:一种有趣的网络流建图方式
当时想出那个建图的方式只是一个偶然的灵感,事后自己感到疑惑,这个做法已经说明网络流具有解决有重合的流量限制问题的潜力,此问题是否仅仅是一个特殊解呢?
经过认真思考,我发现 任意边集流量限制的问题是具有通解的 ,只要通过适当的方式建图,就可以用最大流直接解决,逻辑并不复杂:
最开始我考虑了一个简单的问题:
有三个流,设流量分别为 \\(x,y,z\\),有限制 \\(x+y\\leq a\\), \\(y+z\\leq b\\) ,同时三个流最终流量也有各自的限制,如何建图求出最大流(各自的流先前也有一系列的流量限制,即不一定能做到 \\(x=a,y=0,z=b\\) 的边界情况)。
先设计了 \\(4\\) 个辅助点的图,发现确实能解决两个约束,却同时把最终汇点的入流量给限制小了,把方程列出来,发现问题出在我们关注的流量有 \\(5\\) 个,即 \\(x,y,z,x+y,y+z\\),想要这些变量不互相牵制,显然我们需要至少 \\(5\\) 个参数才行,于是经过调整,我顺利得到了该问题的图:
\\(a,b,c,d,e\\) 为所需的参数,是 \\(5\\) 条边的容量,剩下的边没有容量限制,设为 \\(Inf\\) 即可,得到方程组:
将各自所需的限制带入其中,解出几个参数即可,为了方便,记 \\(\\overline{x}\\) 为流 \\(x\\) 的流量上限,解为:
看起来一切正常,不过这张图还对 \\(x+y+z\\) 作了隐性的限制,在这里
但是通过已有条件的简单不等式变换,应为
两者并不等价!例如 \\(\\overline{x}=1,\\overline{y}=3,\\overline{z}=4,\\overline{x+y}=3,\\overline{y+z}=6\\) 时,取 \\(x=1,y=1,z=4\\) 便是一组反例,这意味着我们之前建图时还是漏掉了一些关键信息,“没有限制” 也是一种性质,需要专门维护。
看来我们只能从完整的限制入手了,\\(x,y,z\\) 三个流共有 \\(7\\) 种限制,这里每一种都建一个点进行维护,画一画之后可以得到如下的图:
此时我们真正地把每一类限制都用参数对应起来了,它的解为:
对于我们没有限制的流量,可以通过解不等式,把其上界算出来,然后作为最大值带入解中,便可以很好地维护这样的关系了。
这样的思路可以拓展到 \\(k\\) 元流的情况,对于每一个大小 \\(>1\\) 的点集,建一个辅助点并向其连有向边,辅助点连向汇点的边容量即为方程参数的解,建成的图辅助点数 \\(2^k-k-1\\),因为有
从而边数为 \\((k+2)\\cdot2^{k-1}-k-1\\),网络流的时间复杂度为 \\(O(k\\cdot2^{2k-1})\\),大约可以 \\(1s\\) 跑规模 \\(k=14\\) 的数据。
附 \\(k=4\\) 的图:
至此,我们用类似于线性规划的思路解决了一个有趣的问题,这个做法本人在百度和 \\(Google\\) 上都没有找到相似的信息,或许仅仅是在 \\(OI\\) 界比较冷门吧,但我们可以拿它来出毒瘤题了诶。
以上是关于Topcoder 13503 ConnectingGame的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 5.6主从Slave_IO_Running:Connecting/error connecting to master *- retry