1 条题解
-
0
自动搬运
来自洛谷,原作者为

guyan
**搬运于
2025-08-24 21:56:14,当前版本为作者最后更新于2020-03-01 11:28:19,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意
给出 个字符串 , 每个字符串包含若干单词 , 可以正着读也可以反着读 , 有些字符串已经确定了阅读方法 , 要求确定剩下字符串的阅读方法 , 使得正反串都出现的单词数最少.
保证对于同一个字符串内的单词 , 要么所有单词的正串字典序大于等于反串 , 要么所有单词的反串字典序大于等于正串.
solution
回文串一定产生 的贡献 , 单独处理
看到正反可以联想到 文理分科 , 类比这道题考虑最小割做法
一开始是想着直接将 个字符串作为被分的对象 , 然而并建不了图 , 因为对于单词来说这是存在性问题
转换思维
查阅题解, 将单词作为被划分集合的对象设源点为 , 汇点为 , 划分的两个集合为 与
将一个单词分为正串与反串两个节点 ( 这里正串定义为正反串中字典序较小的串 ) , 正串向反串连容量为 的边
正串被划分到 集合 正串出现
正串被划分到 集合 正串没有出现
反串被划分到 集合 反串出现
反串被划分到 集合 反串没有出现
当正串一定出现时 , 向正串连容量为 的边
当反串一定出现时 , 反串向 连容量为 的边
接下来考虑那些没有确定读法的字符串
这里有一个重要条件
对于同一个字符串内的单词 , 要么所有单词的正串字典序大于等于反串 , 要么所有单词的反串字典序大于等于正串.
意味着一个字符串中所有出现的单词都是正串 , 或者是反串 ( 这里假设都是正串 )
设使得字符串中单词都是正串的读法是将字符串划入 集合
字符串被划入 集合 正串出现
字符串被划入 集合 反串出现
若字符串被划入 集合 , 其中出现的正串也一定在 集合 , 于是字符串向每个其中出现的单词的正串连 边
若字符串被划入 集合 , 其中出现的反串也一定在 集合 , 于是字符串中出现单词的反串向字符串连 边
code
#include <bits/stdc++.h> using namespace std; const int N = 7e2 + 5; const int M = N * N; const int INF = 0x3f3f3f3f; int _w; int head[N] , eidx , dep[N] , que[N] , he , ta , cur[N] , st , ed; struct Edge { int nxt , to , flow , cap; } edge[M]; void addedge( int u , int v , int cap ) { edge[++eidx] = (Edge) { head[u] , v , 0 , cap }; head[u] = eidx; edge[++eidx] = (Edge) { head[v] , u , 0 , 0 }; head[v] = eidx; } bool bfs( void ) { static int u; memset( dep , 0 , sizeof dep ); dep[que[he = ta = 1] = st] = 1; while( he <= ta ) { u = que[he++]; for( int i = head[u] , v ; ~i ; i = edge[i].nxt ) if( !dep[v = edge[i].to] && edge[i].cap > edge[i].flow ) dep[que[++ta] = v] = dep[u] + 1; } return dep[ed] != 0; } int dfs( int u , int fl ) { if( u == ed || !fl ) return fl; int g = 0 , d; for( int & i = cur[u] ; ~i ; i = edge[i].nxt ) if( dep[edge[i].to] == dep[u] + 1 && ( d = dfs( edge[i].to , min( edge[i].cap - edge[i].flow , fl - g ) ) ) ) { edge[i].flow += d , edge[i ^ 1].flow -= d; if( ( g += d ) >= fl ) break; } return g; } int dinic( void ) { int res = 0; while( bfs() ) { memcpy( cur , head , sizeof head ); res += dfs( st , INF ); } return res; } int n , m , col[N] , row[N] , idx; char str[N][N]; set<string> pal; map<string,int> id; void analyze( string s , int type , int u ) { static int n; static string a , b; n = s.length(); for( int i = 0 , r , flag ; i < n ; i = r + 1 ) { r = i; if( s[i] == '_' ) continue; while( r < n - 1 && s[r + 1] != '_' ) ++r; a = s.substr( i , r - i + 1 ); b = a; reverse( b.begin() , b.end() ); if( b < a ) swap( a , b ) , flag = -1; else flag = 1; if( a == b ) { pal.insert( a ); continue; } if( !id.count( a ) ) { id[a] = ++idx; id[b] = ++idx; addedge( idx - 1 , idx , 2 ); } flag *= type; if( flag == 1 ) addedge( st , id[a] , INF ); else if( flag == -1 ) addedge( id[b] , ed , INF ); else addedge( id[b] , u , INF ) , addedge( u , id[a] , INF ); } } void solve( void ) { _w = scanf("%d%d",&n,&m); idx = n + m; memset( head , -1 , sizeof head ) , eidx = -1; id.clear() , pal.clear(); for( int i = 1 ; i <= n ; ++i ) _w = scanf("%d",row + i ); for( int i = 1 ; i <= m ; ++i ) _w = scanf("%d",col + i ); for( int i = 1 ; i <= n ; ++i ) _w = scanf("%s",str[i]+1); st = 0 , ed = N - 1; static string tmp; for( int i = 1 ; i <= n ; ++i ) { tmp = str[i] + 1; analyze( tmp , row[i] , i ); } for( int i = 1 ; i <= m ; ++i ) { tmp = ""; for( int j = 1 ; j <= n ; ++j ) tmp += str[j][i]; analyze( tmp , col[i] , i + n ); } printf("%d\n",dinic() + int( pal.size() ) ); } int main( void ) { int T; _w = scanf("%d",&T); while( T-- ) solve(); return 0; }
- 1
信息
- ID
- 3002
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者