bzoj3171循环格(费用流)

来源:互联网 发布:香港小鱼儿最近域名 编辑:程序博客网 时间:2024/05/16 18:47

一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位置(r,c)
,你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。
一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。


费用流+二分图模型

我们通过分析题目可得,成为一个完美的循环格,分析其本质,其实就是每一个点入度和出度都必为一。(这是分析题目得到的,以下我会给出一个证明方法)

那么,将每一个格子拆成两个点,一个表示出度,一个表示入度,做全图的最大匹配,就是为每一个点找一个唯一入度和唯一出度

因为转方向需用费用,那么一个点向其原本指向的那个格子费用就是0,其他的格子费用就是1,然后跑费用流


证明(只有每一个点入度出度都唯一才可以有):

首先,每一个格子一定只可能有一个入度,那么总的入度个数就是格子的数量,一个入度产生一个出度,因此出度的总数也是格子的总数

实际上一个格子有可能有多个出度,但是因为总的入度就那么多,假设有一个格子的入度有两个,那么因为总的入度就是格子的数量,说明肯定有格子是没有入度的,所以不可能走到它了。   证毕!


这题不必局限于如何转,而是需要挖掘题目的本质,寻找数学模型

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<cstdlib>#include<queue>using namespace std;const int N=350;const int inf=0x3f3f3f3f;const int qx[]={0,0,1,-1};const int qy[]={1,-1,0,0};int n,m,s,t,id1[20][20],id2[20][20];int dis[1005],head[1005],tot,pre[1005],now[1005];bool b[1005];struct aa{int to,pre,cap,flow,w;}edge[100005];void addedge(int x,int y,int z,int w){edge[++tot].to=y;edge[tot].cap=z;edge[tot].w=w;edge[tot].pre=head[x];head[x]=tot;edge[++tot].to=x;edge[tot].cap=0;edge[tot].w=-w;edge[tot].pre=head[y];head[y]=tot;}bool spfa(){memset(dis,inf,sizeof(dis));memset(b,false,sizeof(b));memset(pre,0,sizeof(pre));memset(now,0,sizeof(now));queue<int> q;q.push(s);dis[s]=0;while(!q.empty()){int u=q.front();q.pop();for (int i=head[u];i;i=edge[i].pre)if (edge[i].cap>edge[i].flow&&dis[edge[i].to]>dis[u]+edge[i].w){dis[edge[i].to]=dis[u]+edge[i].w;now[edge[i].to]=i;pre[edge[i].to]=u;if (!b[edge[i].to]){b[edge[i].to]=true;q.push(edge[i].to);}}b[u]=false;}return dis[t]!=inf;}int work(){int ans=0;while (spfa()){int flow=inf;for (int i=t;i!=s;i=pre[i])flow=min(flow,edge[now[i]].cap-edge[now[i]].flow);ans+=flow*dis[t];for (int i=t;i!=s;i=pre[i])edge[now[i]].flow+=flow,edge[((now[i]-1)^1)+1].flow-=flow;}return ans;}int wx(int x){if (x>n) return 1;if (x<1) return n;return x;}int wy(int y){if (y>m) return 1;if (y<1) return m;return y;}int main(){scanf("%d%d",&n,&m);int bj=0;for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) id1[i][j]=++bj;for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) id2[i][j]=++bj;s=0,t=bj+1;char ch[20];for (int i=1;i<=n;i++){scanf("%s",ch+1);for (int j=1;j<=m;j++){int k;switch(ch[j]){case 'R':k=0;break;case 'L':k=1;break;case 'D':k=2;break;case 'U':k=3;break;}for (int l=0;l<4;l++){int xx=i+qx[l],yy=j+qy[l];xx=wx(xx);yy=wy(yy);int cost;if (k!=l) cost=1;else cost=0;addedge(id1[i][j],id2[xx][yy],1,cost);}}}for (int i=1;i<=n;i++)for (int j=1;j<=m;j++){addedge(s,id1[i][j],1,0);addedge(id2[i][j],t,1,0);}printf("%d",work());return 0;}

总结

1:在思考某些问题的时候,不必局限于如何转,而是需要挖掘题目的本质,寻找适合的数学模型。从而转化为另一个便于解决的问题。



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 药流23天同房了怎么办 小产后不来月经怎么办 人流12天同房了怎么办 人流20天同房了怎么办 人流后5天同房了怎么办 人流后3天同房了怎么办 人流20后同房了怎么办 上环后月经量多怎么办 上环后喝啤酒了怎么办 取环当天同房了怎么办 生完孩子有外痔怎么办 怀孕了宫腔积液怎么办 做完爱4天怕怀孕怎么办 半永久眉失败了怎么办 取环前2天同房了怎么办 怀孕了发现有子宫肌瘤怎么办 宫腔中央性粘连怎么办 孕妇宫腔粘连带怎么办 内膜厚怎么办吃什么好 功血引起的贫血怎么办 吃宫血宁后月经不来了怎么办 孕晚期胎心不好怎么办 胎心不好怎么办让住院 39 5胎心不好怎么办 肺长了一个肿瘤怎么办 血糖高伤口不愈合怎么办 有轻微的狐臭该怎么办 嘴上汗毛太重怎么办 风把裙子吹起怎么办 每天三四点醒来就睡不着怎么办 打游戏手汗太多怎么办 老年人胸闷气短呼吸困难怎么办 冒险岛宠物饿了怎么办 不小心喝了黑墨怎么办 压的双眼皮开了怎么办 割了双眼皮显老怎么办 心脏供血不足怎么办呢 怀孕了哮喘犯了怎么办 喝了电解质不拉怎么办 喝电解质散吐了怎么办 皮鞋买大了一号怎么办