一个图的生成树,即找出这个无向连通图的某个边集为一棵树
最小生成树,即使边权和最小(最大生成树同理)
设已加入最小生成树的点集为T
算法思路:每次寻找T所连向的不在T中的点的边中最小的边,将此边和点加入T
实现
int Prim(){
memset(cost,-1,sizeof cost);
cost[1]=0;//起始点设谁都可以
int f=1;
int ans=0;
while(fint u=-1;
for(int i=1;i<=n;i++)
if(!vis[i]&&cost[i]!=-1&&(cost[i]1))
u=i;
if(u==-1)
break;
vis[u]=1;
ans+=cost[u];
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v,w=edge[i].w;
if(cost[v]==-1||wreturn ans;
}
算法思路:将边按权值排序,从小到大枚举,若两端点还未联通,则加入此边(用并查集实现)
代码?
看例题1
当新加入的边是(u,v)时,找到当前u到v路径上最大的边,若权值大于新边,则替换之
题意:裸的最小生成树
#include
#include
#include
using namespace std;
int n;
struct Edge{
int s,e,w;
Edge(int ss,int ee,int ww):s(ss),e(ee),w(ww){}
Edge(){}
bool operator <(const Edge &e1) const{
return wvector edge;
vector<int > father;
int getroot(int x){
if(father[x]==x)
return x;
father[x]=getroot(father[x]);
return father[x];
}
void merge(int x,int y){
int f1=getroot(x),f2=getroot(y);
if(f1==f2)
return ;
father[f2]=f1;
}
int main(){
while(~scanf("%d",&n)){
father.clear();
edge.clear();
for(int i=0;ifor(int i=0;ifor(int j=0;jint w;
scanf("%d",&w);
edge.push_back(Edge(i,j,w));
}
sort(edge.begin(),edge.end());
int done=0,len=0;
for(int i=0;iif(getroot(edge[i].s)!=getroot(edge[i].e))
{
merge(edge[i].s,edge[i].e);
done++;
len+=edge[i].w;
}
if(done==n-1)
break;
}
printf("%d\n",len);
}
}
题意:最大生成树
#include
#include
#include
using namespace std;
int fa[1005];
int find_father(int x){
if(x==fa[x])
return x;
return fa[x]=find_father(fa[x]);
}
struct node{
int x,y,c;
}edge[20005],p;
bool cmp(node a,node b){
return a.c>b.c;
}
int down;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
p.x=a,p.y=b,p.c=c;
edge[i]=p;
}
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
p=edge[i];
int f1=find_father(p.x),f2=find_father(p.y);
if(f1==f2)
continue ;
fa[f1]=f2;
ans+=p.c;
down++;
}
if(down==n-1)
printf("%d",ans);
else
printf("-1");
}
题意:求最小生成树上最长的边
#include
#include
#include
using namespace std;
int fa[1005];
int find_father(int x){
if(x==fa[x])
return x;
return fa[x]=find_father(fa[x]);
}
struct node{
int x,y,c;
}edge[20005],p;
bool cmp(node a,node b){
return a.c>b.c;
}
int down;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
p.x=a,p.y=b,p.c=c;
edge[i]=p;
}
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
p=edge[i];
int f1=find_father(p.x),f2=find_father(p.y);
if(f1==f2)
continue ;
fa[f1]=f2;
ans+=p.c;
down++;
}
if(down==n-1)
printf("%d",ans);
else
printf("-1");
}
Er…前三道好像都是版题
那就来个稍微强一点的吧
题意:某地区共有n座村庄,每座村庄的坐标用一对整数(x,y)表示,现在要在村庄之间建立通讯网络。
• 通讯工具有两种,分别是需要铺设的普通线路和无线通讯的卫星设备。
• 只能给k个村庄配备卫星设备,拥有卫星设备的村庄互相间直接通讯。
• 铺设了线路的村庄之间也可以通讯。但是由于技术原因,两个村庄之间线路长度最多不能超过 d
已知所有村庄的坐标 ( x , y ) ,卫星设备的数量 k 。
问:如何分配卫星设备,才能使各个村庄之间能直接或间接的通讯,并且 d 的值最小?求出 d 的最小值。
数据规模:0 <= k <= n<= 500
假设 d 已知,把所有铺设线路的村庄连接起来,构成一个图。需要卫星设备的台数就是图的连通分量的个数
显然d越小,连通分量越多
其实呢,说白了,就是完全图最小生成树第K大的边长
因为:最小生成树中的最长k-1条长边都去掉后,正好将原树分成了k 个连通分支,在每个连通分支上摆一个卫星设备即可,而剩下图中最长长度就是第k大的边
这么说来好像还是版题
突然,一个诡异的问题出现了
一个图可能有多个最小生成树,它们第K大的边难道都一样长?
巧了,它们确实一样长
我们有这样一个结论
一个图的两棵最小生成树,边的权值序列排序后结果相同
什么?你要证明?
我已经发现了这个结论的一个奇妙的证明,由于这里篇幅太小,写不下
——Pierre de Fermat
另解:
我们用度限制最小生成树的办法,新建一个超级点,对每个点连一条权值为0的边,然后限制它度数不大于k
给你一个n(n<=10^5)个点, m条边(n-1<=m <= min(10^5, n*(n-1)/2))的无向连通图(任何两个点之间只有一条边)。给出每条边的两个端点和对应的权值。对于图中的每一条边,判断
(1)存在于任何一颗最小生成树中 any
(2)至少存在于某一颗最小生成树中 at least one
(3)不存在任何一棵最小生成树中 none
假设所有边权值都相同,那么割边是any,其他的边是at least one
假设所有边的权值都不相同,那么我们按着kruscal的方法构造最小生成树即可
那么回到这个问题,其实我们要做的就是把上面的方法综合一下
相同权值的边一起处理,不同权值的边按kruscal来做
#include
#include
#include
using namespace std;
const int N=100005,M=200005;
struct node{
int u,v,next,pos,rev;
bool ban;
}edge[M];
struct node1{
int u,v,w,pos;
}e[M];
int n,m;
int dfn[N],low[N],time;
int ans[M];
int fa[N];
int head[N],cnt;
bool cmp(node1 a,node1 b){
return a.wint u,int v,int pos,int rev){
cnt++;
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].pos=pos;
edge[cnt].rev=rev;
edge[cnt].next=head[u];
edge[cnt].ban=0;
head[u]=cnt;
}
int find_father(int x){
if(x==fa[x])
return x;
return fa[x]=find_father(fa[x]);
}
void merge(int x,int y){
int f1=find_father(x),f2=find_father(y);
if(f1==f2)
return ;
fa[f1]=f2;
}
void tarjan(int u){
time++;
dfn[u]=low[u]=time;
for(int i=head[u];i;i=edge[i].next){
if(edge[i].ban)
continue ;
edge[i].ban=1;
edge[edge[i].rev].ban=1;
int v=edge[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]pos]!=-2)
ans[edge[i].pos]=1;
}
else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
e[i].pos=i;
}
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
cnt=0;
int j=i;
while(j<m&&e[j].w==e[j+1].w) j++;
for(int k=i;k<=j;k++){
if(find_father(e[k].u)!=find_father(e[k].v)){
add_edge(fa[e[k].u],fa[e[k].v],e[k].pos,cnt+2);
add_edge(fa[e[k].v],fa[e[k].u],e[k].pos,cnt);
ans[e[k].pos]=-1;
}
}
time=0;
for(int k=i;k<=j;k++){
if(ans[e[k].pos]!=0&&!dfn[fa[e[k].u]])
tarjan(fa[e[k].u]);
}
for(int k=i;k<=j;k++){
if(ans[e[k].pos]!=0){
merge(e[k].u,e[k].v);
dfn[fa[e[k].u]]=dfn[fa[e[k].v]]=0;
head[fa[e[k].u]]=head[fa[e[k].v]]=0;
}
}
i=j;
}
for(int i=1;i<=m;i++){
if(ans[i]==1)
printf("any\n");
else if(ans[i]==-1)
printf("at least one\n");
else
printf("none\n");
}
}
题目大意:找到一棵严格的次小生成树。保证存在。
结论:次小生成树一定能由最小生成树替换一条边得到
有了这个结论,我们先跑一遍最小生成树,然后对于不在最小生成树中的边,我们考虑替换它会增长多少,然后对增长量取最小值即可
注意到必须是严格的次小生成树,如果路径上的最长边等于我们将要加入的边,那么我们计算的增加量不应该是0,而应该是这条边减去严格的次大边
所以具体实现上,树上RMQ,储存最大值和严格的次大值
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100005,M=600005,L=20;
struct node{
int u,v,w,nxt;
int pos;
}e[M],edge[M];
int head[N],mcnt;
void add_edge(int u,int v,int w,int pos){
mcnt++;
edge[mcnt].u=u;
edge[mcnt].v=v;
edge[mcnt].w=w;
edge[mcnt].pos=pos;
edge[mcnt].nxt=head[u];
head[u]=mcnt;
}
int fa[N];
bool tree[M];
bool cmp(node a,node b){
return a.wint find_father(int x){
if(fa[x]==x)
return x;
return fa[x]=find_father(fa[x]);
}
void merge(int x,int y){
int f1=find_father(x),f2=find_father(y);
fa[f1]=f2;
}
ll ans;
int n,m;
void Kruscal(){
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
if(find_father(u)!=find_father(v)){
merge(u,v);
ans+=e[i].w;
tree[e[i].pos]=1;
}
}
}
int max1[N][L+2],max2[N][L+2];
int pa[N][L+2];
int deep[N];
bool vis[N];
void dfs(int u,int fa,int d){
deep[u]=d;
vis[u]=1;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v,w=edge[i].w;
if(!tree[edge[i].pos]||v==fa)
continue ;
pa[v][0]=u;
max1[v][0]=w;
dfs(v,u,d+1);
}
}
void pre(){
dfs(1,-1,1);
for(int i=1;i<=L;i++)
for(int j=1;j<=n;j++){
pa[j][i]=pa[pa[j][i-1]][i-1];
max1[j][i]=max(max1[j][i-1],max1[pa[j][i-1]][i-1]);
max2[j][i]=max(max2[j][i-1],max2[pa[j][i-1]][i-1]);
if(max1[j][i-1]>max1[pa[j][i-1]][i-1]&&max1[pa[j][i-1]][i-1]>max2[j][i])
max2[j][i]=max1[pa[j][i-1]][i-1];
if(max1[pa[j][i-1]][i-1]>max1[j][i-1]&&max1[j][i-1]>max2[j][i])
max2[j][i]=max1[j][i-1];
}
}
int LCA(int x,int y){
if(deep[x]int i=0;
while(1<for(int j=i;j>=0;j--)
if(deep[x]-(1<=deep[y])
x=pa[x][j];
if(x==y)
return x;
for(int j=L;j>=0;j--)
if(pa[x][j]!=pa[y][j])
x=pa[x][j],y=pa[y][j];
return pa[x][0];
}
int calc(int x,int y,int len){
int lca=LCA(x,y);
int i=0;
while(1<int res=0;
for(int j=i;j>=0;j--)
if(deep[x]-(1<=deep[lca]){
if(max1[x][j]!=len)
res=max(res,max1[x][j]);
else
res=max(res,max2[x][j]);
x=pa[x][j];
}
i=0;
while(1<for(int j=i;j>=0;j--)
if(deep[y]-(1<=deep[lca]){
if(max1[y][j]!=len)
res=max(res,max1[y][j]);
else
res=max(res,max2[y][j]);
y=pa[y][j];
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[i].u=u,e[i].v=v,e[i].w=w,e[i].pos=i;
add_edge(u,v,w,i);
add_edge(v,u,w,i);
}
Kruscal();
pre();
ll add=1<<30;
for(int i=1;i<=m;i++)
if(!tree[e[i].pos]){
ll ff=calc(e[i].u,e[i].v,e[i].w);
if(ff>0&&e[i].w-ff>0)
add=min(add,e[i].w-ff);//
}
printf("%lld\n",ans+add);
}
题目大意:带权无向图,Park节点要求度数必须<=k,求满足这个限制的最小生成树
我们先删掉根节点
再对剩下的图跑最小生成森林
设这个森林有m个联通块,那么根节点的度数至少为m,如果m>k我们可以直接判定不可能
我们将根节点与连接到各个联通块最短的边连起来,我们就得到了一个m度的最小生成树
那么现在我们的问题是,已经得到了一个x度最小生成树,怎么求一个x+1度最小生成树
我们可以枚举每个与根节点相邻而这条边还未加入生成树的边
然后通过dp来计算添加这条边增加的最小权值(即环上最长的边)
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=25,M=1000;
const int INF=0x3f3f3f3f;
struct node{
int u,v,w;
}e[M],dp[N];
int ecnt;
bool cmp(node a,node b){
return a.wint fa[N];
bool ontree[N][N];
int G[N][N];
int ans;
int n,m,k;
int find_father(int x){
if(x==fa[x])
return x;
return fa[x]=find_father(fa[x]);
}
void merge(int x,int y){
int f1=find_father(x),f2=find_father(y);
fa[f1]=f2;
}
void Kruskal(){
sort(e+1,e+ecnt+1,cmp);
for(int i=1;i<=ecnt;i++){
int u=e[i].u;
int v=e[i].v;
if(u==1||v==1) continue ;
if(find_father(u)!=find_father(v)){
merge(u,v);
ontree[u][v]=ontree[v][u]=1;
ans+=e[i].w;
}
}
}
void dfs(int u,int fa){
for(int v=2;v<=n;v++){
if(v==fa||!ontree[u][v])
continue ;
if(dp[v].w==-1){
if(dp[u].w>G[u][v])
dp[v]=dp[u];
else{
dp[v].u=u;
dp[v].v=v;
dp[v].w=G[u][v];
}
}
dfs(v,u);
}
}
int minedge[N];
void solve(){
int point[N];
for(int i=2;i<=n;i++)
if(G[1][i]!=INF){
int color=find_father(i);
if(minedge[color]>G[1][i]){
minedge[color]=G[1][i];
point[color]=i;
}
}
for(int i=1;i<=n;i++)
if(minedge[i]!=INF){
m++;
ontree[1][point[i]]=ontree[point[i]][1]=1;
ans+=G[1][point[i]];
}
for(int i=m+1;i<=k;i++){
memset(dp,-1,sizeof dp);
dp[1].w=-INF;
for(int j=2;j<=n;j++)
if(ontree[1][j])
dp[j].w=-INF;
dfs(1,-1);
int idx,minnum=INF;
for(int j=2;j<=n;j++){
if(minnum>G[1][j]-dp[j].w){
minnum=G[1][j]-dp[j].w;
idx=j;
}
}
if(minnum>=0)
break;
ontree[1][idx]=ontree[idx][1]=1;
ontree[dp[idx].u][dp[idx].v]=ontree[dp[idx].v][dp[idx].u]=0;
ans+=minnum;
}
}
map<string,int>dict;
int main()
{
int name;
scanf("%d",&name);
string s1,s2;
memset(minedge,0x3f,sizeof minedge);
memset(G,0x3f,sizeof G);
dict["Park"]=++n;
for(int i=0;ifor(int i=1;i<=name;i++){
cin>>s1>>s2;
int d;scanf("%d",&d);
if(!dict[s1]) dict[s1]=++n;
if(!dict[s2]) dict[s2]=++n;
int u=dict[s1],v=dict[s2];
ecnt++;
e[ecnt].u=u;
e[ecnt].v=v;
e[ecnt].w=d;
G[u][v]=G[v][u]=min(G[u][v],d);
}
scanf("%d",&k);
Kruskal();
solve();
printf("Total miles driven: %d\n",ans);
}
平面上有n个点,每个点有一个h,对于每条边而言,长度为直线距离,费用为h值之差,现在要求找到一个生成树,使得总费用与总长度的比值最小
01分数规划
二分答案为x
则对于
#include
#include
#include
#include
#include
using namespace std;
const int N=1005;
const double INF=23333333333333.0;
int x[N],y[N],h[N],n;
double dist[N];
bool vis[N];
double dh[N][N],dd[N][N];
double Prim(double mid){
for(int i=1;i<=n;i++)
dist[i]=INF;
dist[1]=0;
memset(vis,0,sizeof vis);
vis[1]=1;
int u=1;
double ans=0;
for(int i=1;idouble mincost=INF;
int minv;
for(int v=1;v<=n;v++)
if(!vis[v]){
double len=dh[u][v]-mid*dd[u][v];
if(lenif(dist[v]1;
u=minv;
}
return ans;
}
void pre(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
dh[i][j]=fabs(h[i]-h[j]);
dd[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
}
int main()
{
while(~scanf("%d",&n)&&n){
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x[i],&y[i],&h[i]);
}
pre();
double l=0,r=10000000,mid;
int T=50;
while(T--){
mid=(l+r)/2;
if(Prim(mid)>0){
l=mid;
}
else{
r=mid;
}
}
printf("%.3f\n",r);
}
}
求两种类型的边出现次数相同的生成树
首先显然n必须是奇数
先把两种边分别编号为0、1
然后跑最小生成树,那么现在我们得到的生成树是满足有尽可能多的0边的
若此时生成树的权值已经大于了(n-1)/2,那么就无解了
然后对于每条1边(u,v),我们看加入u到v路径上有没有权值为0的边,如果有,则替换之,然后生成树权值++,重复此操作,至权值为(n-1)/2
#include
#include
#include
#include
using namespace std;
const int M=1e6,N=1e4;
struct node{
int u,v,w,pos,nxt,rev;
bool vis;
}e[M],edge[M];
bool cmp(node a,node b){
return a.wint head[N],mcnt;
bool tree[M];
void add_edge(int u,int v,int w,int pos,int rev){
mcnt++;
edge[mcnt].u=u;
edge[mcnt].v=v;
edge[mcnt].w=w;
edge[mcnt].pos=pos;
edge[mcnt].rev=rev;
edge[mcnt].nxt=head[u];
head[u]=mcnt;
}
int fa[N];
int find_father(int x){
if(x==fa[x])
return x;
return fa[x]=find_father(fa[x]);
}
void merge(int x,int y){
int f1=find_father(x),f2=find_father(y);
fa[f1]=f2;
}
bool poquan(int u,int t,int& id){
if(u==t)
return 1;
for(int i=head[u];i;i=edge[i].nxt){
if(edge[i].vis)
continue ;
edge[i].vis=1;
edge[edge[i].rev].vis=1;
bool ok;
ok=poquan(edge[i].v,t,id);
edge[i].vis=0;
edge[edge[i].rev].vis=0;
if(ok){
if(!edge[i].w)
id=i;
return 1;
}
}
return 0;
}
int main()
{
int n,m;
int mmp;
scanf("%d%d",&n,&m);
mmp=m;
if((n-1)&1){
puts("-1");
return 0;
}
int sum=(n-1)/2;
for(int i=1,j=1;i<=m;i++,j++){
int u,v,w;
char c;
scanf("%d%d %c",&u,&v,&c);
if(c=='S')
w=1;
else
w=0;
e[i].u=u,e[i].v=v,e[i].w=w,e[i].pos=j;
if(u==v){
i--;
m--;
continue ;
}
}
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)
fa[i]=i;
int tot=0,k=0;
for(int i=1;i<=m&&k1;i++){
int u=e[i].u,v=e[i].v,w=e[i].w,pos=e[i].pos;
if(find_father(u)!=find_father(v)){
merge(u,v);
k++;
tot+=w;
tree[e[i].pos]=1;
add_edge(u,v,w,pos,mcnt+2);
add_edge(v,u,w,pos,mcnt);
e[i].vis=1;
}
}
if(tot>sum){
puts("-1");
return 0;
}
for(int i=1;i<=m&&totif(e[i].w==0)
continue ;
int u=e[i].u,v=e[i].v,pos=e[i].pos;
if(e[i].vis)
continue ;
int id=0;
poquan(u,v,id);
if(!id)
continue;
tot++;
tree[e[i].pos]=1;
tree[edge[id].pos]=0;
edge[id].vis=1;
edge[edge[id].rev].vis=1;
add_edge(u,v,1,pos,mcnt+2);
add_edge(v,u,1,pos,mcnt);
}
if(tot"-1");
return 0;
}
/*find_father(1);
for(int i=2;i<=n;i++)
if(find_father(i)!=fa[1]){//!
printf("-1");
return 0;
}*/
printf("%d\n",n-1);
for(int i=1;i<=mmp;i++)
if(tree[i]){
printf("%d ",i);
}
}
题目大意:给出一个n*m的格子,询问使给出的k个点联通的最小花费
(这个是点权)
n,m<=10,k<=min(10,n*m)
dp(i,S)表示当前在i,已连接状态为S的最小花费
转移:
第一种: d(i,S)=min(d(i,x)+d(i,S−x)−c(i)) d ( i , S ) = m i n ( d ( i , x ) + d ( i , S − x ) − c ( i ) ) (减去c(i)是因为这是点权,c(i)算重了)
第二种: d(i,S)=min(d(j,S−v[i])+c(i)) d ( i , S ) = m i n ( d ( j , S − v [ i ] ) + c ( i ) ) (v[i]为i号节点状压的值,若i不在那k个点中则为0
枚举子集的技巧:for(sub=(state-1)&state;sub;sub=(sub-1)&state) sub即为state的一个子集
然后呢,第二种转移可以用SPFA来实现
而且这两个转移可以分开计算
#include
#include
#include
#include
using namespace std;
const int M=1025;
const int N=15;
int x[N],y[N];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
const int INF=0x3f3f3f3f;
struct Point{
int x,y;
Point(){}
Point(int _x,int _y){
x=_x;
y=_y;
}
}p[N*M];
struct State{
int s,x,y;
State(){}
State(int _s,int _x,int _y){
s=_s,x=_x,y=_y;
}
}pre[M][N][N];
int n,m;
int d[M][N][N],mat[N][N];
int k,pcnt,state,S;
bool vis[N][N];
queue q1,q2;
void Init(){
scanf("%d%d",&n,&m);
memset(d,0x3f,sizeof d);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&mat[i][j]);
if(!mat[i][j]){
d[1<0;
k++;
}
}
S=(1<1;
}
bool cmp(Point a,Point b){
return d[state][a.x][a.y]void SPFA(){
Point u;
sort(p+1,p+pcnt+1,cmp);
for(int i=1;i<=pcnt;i++){
q1.push(p[i]);
vis[p[i].x][p[i].y]=true;
}
while((!q1.empty())||(!q2.empty())){
if(q1.empty())
u=q2.front(),q2.pop();
else if(q2.empty())
u=q1.front(),q1.pop();
else{
Point u1=q1.front(),u2=q2.front();
if(d[state][u1.x][u1.y]else
u=u2,q2.pop();
}
vis[u.x][u.y]=false;
for(int i=0;i<4;i++){
int x=u.x+dir[i][0],y=u.y+dir[i][1];
if(x<=0||y<=0||x>n||y>m)
continue ;
if(d[state][x][y]>d[state][u.x][u.y]+mat[x][y]){
d[state][x][y]=d[state][u.x][u.y]+mat[x][y];
pre[state][x][y]=State(state,u.x,u.y);
if(!vis[x][y]){
q2.push(Point(x,y));
vis[x][y]=true;
}
}
}
}
}
void Steiner_tree(){
for(state=1;state<=S;state++){
pcnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
for(int s=(state-1)&state;s;s=(s-1)&state){
if(d[state][i][j]>d[s][i][j]+d[state-s][i][j]-mat[i][j]){
d[state][i][j]=d[s][i][j]+d[state-s][i][j]-mat[i][j];
pre[state][i][j]=State(s,i,j);
}
}
if(d[state][i][j]!=INF){
pcnt++;
p[pcnt]=Point(i,j);
}
}
SPFA();
}
}
void dfs(int x,int y,int s){
if(pre[s][x][y].s==0)
return ;
vis[x][y]=true;
int xx=pre[s][x][y].x,yy=pre[s][x][y].y,ss=pre[s][x][y].s;
if(xx==x&&yy==y){
dfs(x,y,ss);
dfs(x,y,s-ss);
}
else
dfs(xx,yy,ss);
}
void Print(){
Point u;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!mat[i][j]){
u.x=i,u.y=j;
break;
}
printf("%d\n",d[S][u.x][u.y]);
dfs(u.x,u.y,S);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!mat[i][j])
putchar('x');
else if(vis[i][j])
putchar('o');
else
putchar('_');
}
puts("");
}
}
int main()
{
Init();
Steiner_tree();
Print();
}