【km算法复习】 game矩形填数


     

       貌似以前学的km算法都是背的=。=!一直有没怎么用,都忘记到爪哇国去了,这次赶快复习一下。

        km什么的,就是通过设定顶标,慢慢压低定标,使得有足够的边进入相等子图,最后得到一个最大权匹配。

      

       无脑裸代码就不贴了。


        一道不求最大权匹配却使用km算法的题目:

        给定一个n*m的矩阵,给矩阵的每个格子中填上数字,要求:1,相邻格子的数字之和大于等于他们的相邻的边的权值 。 2 .填的数字之和最小。

 

       把矩阵黑白染色,建立二分图,格子的相邻边作为二分图的边,然后求一组km的顶标,定标的答案就是填的数字。


      

# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>

using namespace std;

const int oo=1073741819, maxn = 100*100;
int h[105][105],lx[maxn],ly[maxn],slack[maxn], point[maxn], top,linke[maxn*3], sum[maxn*3], wis[maxn*3], next[maxn*3];
bool vx[maxn], vy[maxn];
int ans,t1,t2,n,m;

inline int max(int x, int y) {return x>y?x:y;};
inline int min(int x, int y) {return x<y?x:y;};

void link(int x, int y, int z)
{
	++top; next[top]=linke[x]; linke[x]= top; sum[top]=y; wis[top]=z;
	lx[x]=max(lx[x], z);
}

bool find(int x)
{
	vx[x]=true;
	for (int ke=linke[x]; ke!=0;ke=next[ke])
	{
		int d=lx[x]+ly[sum[ke]]-wis[ke];
		if ((!vy[sum[ke]])&& (!d))
		{
			vy[sum[ke]]=true;
			if (point[sum[ke]]==0|| find(point[sum[ke]]))
			{point[sum[ke]]=x; return true;};
		} else slack[sum[ke]]=min(slack[sum[ke]], d);
	}
	return false;
}

int main()
{
	int i,j,x;
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
	scanf("%d%d", &n, &m);
	for (i = 1; i <= n; i++) 
	  for (j = 1; j <= m; j++) 
	    if (i+j&1) h[i][j]=++t1; else h[i][j]=++t2;
	for (i = 1; i <= n; i++)
	{
	  for (j = 1; j <= m; j++)
	  if (i+j&1)
	  {
			scanf("%d", &x); if (i<n)link(h[i][j], h[i+1][j], x);
			scanf("%d", &x); if (j<m)link(h[i][j], h[i][j+1], x);
	  }
	  else
	  {
			scanf("%d", &x); if (i<n)link(h[i+1][j], h[i][j], x);
			scanf("%d", &x); if (j<m)link(h[i][j+1], h[i][j], x);
	  }
    }
    int d;
    for (i = 1; i <= t1; i++)
		for (;;)
		{
			memset(vx,0,sizeof(vx)); memset(vy,0,sizeof(vy));memset(slack,127,sizeof(slack));
			if (find(i)) break;
			for (d=oo,j=1;j<=t2;j++) if (!vy[j]) d=min(d,slack[j]);
			for (j=1;j<=t1;j++) if (vx[j]) lx[j]-= d;
			for (j=1;j<=t2;j++) if (vy[j]) ly[j]+= d;
		}
	for (ans=0,i=1;i<=n;i++)
	  for (j=1;j<=m;j++)
	  if (i+j&1) ans+= lx[h[i][j]]; else ans+= ly[h[i][j]];
	printf("%d\n", ans);
	for (i=1;i<=n;i++)
	{
	  for (j=1;j<=m; j++)
	    if (i+j&1) printf("%d ",lx[h[i][j]]); else printf("%d ", ly[h[i][j]]);
	  printf("\n");
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值