luogu

loj

题意看了我半天(逃 (应该是我语文太差了)

题意是要确定每一行和每一列的看单词的顺序,使得同时正着出现和反着出现在里面的单词数量最少,每行和每列的性质是这一行所有单词反过来的单词要么字典序大于等于原来的,要么小于等于原来的

首先回文单词一定会出现,可以直接加入答案,以后就不用管了.对于剩下的单词,如果不想让他贡献答案,那就要使的每次读这个单词都是一个样子,例如"ABC" "CBA",那么第一个正着读,第二个反着读答案最小.为了方便,我们只保留单词字典序更小的形式,同时把行列的读到单词字典序更小的方向作为正方向.然后现在问题变成给每一行每一列确定方向,使得所在行列同时有正方向和反方向的单词数量最小(就是给每个集合确定黑/白颜色,使得所在集合中有黑集合和白集合的元素个数最小)

这个可以使用最小割解决,具体是给每行每列建点,如果正方向可以用就从原点\(ps\)向这个点\(i\)连容量为\(1\)的边\((ps,i,1)\),否则连\((ps,i,+\infty)\),如果反方向可以用就从\(i\)向汇点\(pt\)连容量为\(1\)的边\((i,pt,1)\),否则连\((i,pt,+\infty)\),这样如果最后\(i\)在\(pt\)集合就是正方向,否则就是反方向.然后对于每个单词\(j\)建两个点\(a1_j,a2_j\),连边\((ps,a1_j,p),(a2_j,pt,p)(p\)为一个大于\(n+m\)的数\()\),然后对于单词\(j\)所在的行列\(i\),连边\((a1_j,i,+\infty),(i,a2_j,+\infty)\),这样如果一个单词所在行列同时割在\(ps\)或者\(pt\)集合中,就要多割去一个\(p\),不然要多割掉\(2p\)并且给答案加上\(2\)(一个非回文单词要统计两次).最后答案为回文单词个数+\(2(\lfloor\frac{flow}{p}\rfloor-\)非回文单词个数\()\)

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long using namespace std;
const int N=75,M=N*N*2,inf=1<<29;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
return x*w;
}
int to[M*4],nt[M*4],c[M*4],hd[M],tot=1;
void add(int x,int y,int z)
{
++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
}
int ps,pt,nhd[M],lv[M];
queue<int> q;
bool bfs()
{
for(int i=ps;i<=pt;++i) lv[i]=0;
lv[ps]=1,q.push(ps);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(c[i]>0&&!lv[y])
{
lv[y]=lv[x]+1;
q.push(y);
}
}
}
return lv[pt];
}
int dfs(int x,int fw)
{
if(x==pt) return fw;
int an=0;
for(int &i=nhd[x];i;i=nt[i])
{
int y=to[i];
if(c[i]>0&&lv[y]==lv[x]+1)
{
int dt=dfs(y,min(fw,c[i]));
c[i]-=dt,c[i^1]+=dt;
fw-=dt,an+=dt;
if(!fw) break;
}
}
return an;
}
int dinic()
{
int an=0,dt=0;
while(bfs())
{
for(int i=ps;i<=pt;++i) nhd[i]=hd[i];
while((dt=dfs(ps,inf))) an+=dt;
}
return an;
}
map<string,int> mp;
vector<int> ls[N*N];
string ss[N*N];
char cc[N][N];
int n,m,ans,w1[N],w2[N],tt; int main()
{
int T=rd();
while(T--)
{
memset(hd,0,sizeof(hd)),tot=1;
n=rd(),m=rd();
ps=0,pt=n+m+n*m+1;
for(int i=1;i<=n;++i) w1[i]=rd();
for(int i=1;i<=m;++i) w2[i]=rd();
for(int i=1;i<=n;++i) scanf("%s",cc[i]+1);
mp.clear(),tt=0;
for(int i=1;i<=n;++i)
{
string nw,rnw;
bool fg=0;
for(int j=1;j<=m+1;++j)
{
if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
else if(!nw.empty())
{
rnw=nw;
reverse(rnw.begin(),rnw.end());
if(!fg&&rnw!=nw)
{
fg=1;
if(rnw<nw) nw=rnw,w1[i]=-w1[i];
}
if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
ls[mp[nw]].push_back(i);
nw.clear();
}
}
}
for(int j=1;j<=m;++j) cc[n+1][j]=0;
for(int j=1;j<=m;++j)
{
string nw,rnw;
bool fg=0;
for(int i=1;i<=n+1;++i)
{
if(cc[i][j]&&cc[i][j]!='_') nw.push_back(cc[i][j]);
else if(!nw.empty())
{
rnw=nw;
reverse(rnw.begin(),rnw.end());
if(!fg&&rnw!=nw)
{
fg=1;
if(rnw<nw) nw=rnw,w2[j]=-w2[j];
}
if(!mp.count(nw)) mp[nw]=++tt,ss[tt]=nw,ls[tt].clear();
ls[mp[nw]].push_back(j+n);
nw.clear();
}
}
}
for(int i=1;i<=n;++i)
{
add(ps,i,w1[i]>=0?1:inf);
add(i,pt,w1[i]<=0?1:inf);
}
for(int j=1;j<=m;++j)
{
add(ps,j+n,w2[j]>=0?1:inf);
add(j+n,pt,w2[j]<=0?1:inf);
}
int pc=n+m,dt=0;
ans=0;
for(int i=1;i<=tt;++i)
{
string rnw=ss[i];
reverse(rnw.begin(),rnw.end());
if(ss[i]==rnw){++ans;continue;}
dt+=233;
++pc,++pc;
add(ps,pc-1,233),add(pc,pt,233);
sort(ls[i].begin(),ls[i].end());
vector<int>::iterator it;
int la=0;
for(it=ls[i].begin();it!=ls[i].end();++it)
{
int x=*it;
if(x==la) continue;
add(pc-1,x,inf),add(x,pc,inf);
la=x;
}
}
ans+=((dinic()-dt)/233)*2;
printf("%d\n",ans);
}
return 0;
}

最新文章

  1. 全栈开发必备的10款 Sublime Text 插件
  2. clang 搭建和编译boost 和zero ICE库 (Ubuntu10 64)
  3. centos 开启VNC
  4. 为Unity项目生成文档(一)
  5. iOS汉字中提取首字母
  6. Activity中与ListActivity中使用listview区别
  7. 剑指offer-第二章算法之斐波拉契数列(青蛙跳台阶)
  8. C#图解教程读书笔记(第15章 委托)
  9. iOS 自定义view里实现控制器的跳转
  10. usb device selection
  11. JavaScript中的apply与call与arguments对象
  12. apache 创建虚拟目录
  13. 前端构建利器Grunt—Bower
  14. 201521123004《Java程序设计》第4周学习总结
  15. openstack常用命令
  16. 理解ValueStack的基本机制 OGNL表达式
  17. pathon之多线程详解
  18. Hadoop-2.2.0中文文档—— Shell命令
  19. 51nod 1806 wangyurzee的树
  20. JS及Dom练习 | 模态对话框及复选框操作

热门文章

  1. js判断某个字符串是否包含另一个字符串
  2. MongoDB数据库的基本操作命令
  3. 【Spark机器学习速成宝典】基础篇01Windows下spark开发环境搭建+sbt+idea(Scala版)
  4. 兼容ie9以下支持媒体查询和html5
  5. flutter 网络请求以及数据处理
  6. Selenium 2自动化测试实战19(下载文件)
  7. linux常用命令(22)gzip命令
  8. java.lang.ClassNotFoundException: org.apache.commons.collections.FastHashMap
  9. Java基础面试题集(二)
  10. java:Spring框架2(bean的作用域,静态工厂和实例工厂,自动装配,动态代理)