2016 Multi-University Training Contest 1 1007 Rigid Frameworks

本文探讨了一个关于如何通过增加对角线来固定网格图的问题。关键在于理解如何将问题转化为二分图的联通性问题,并使用动态规划进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击打开链接

题目大意:有一个n*m的网格图,加几条斜边可以使它固定。

解题思路;看到这题真是一脸蒙圈啊,比赛后看了不少东西才勉强明白,对于一个网格图来说要使它固定要求是n行和m列联动。

当你在(i,j)上加入一条对角线时,第i行和第j列联动;当你同时在(i,j)和(i,k)加入对角线的时候,第i行、第j列、第k列联动;

那么问题就可以简化成两堆点,左边是n个点表示n行,右边是m个点表示m列,每作一条边表示第i行和第j列联动,只要使这个二分图完全联通就表示n行m列联动。

也就是上面这种情况,但是同时也存在下面这种情况。

这种情况明显是不联通的,所以到去掉。

对于总情况dp[n][m][k](n行,m列,k个格子有对角线)=c[n*m][k](组合数,在n*m中选k个格子)

但是对于ii行(ii<=i)jj列(jj<=j)联通但是其他情况不联通的情况,我们要去掉,那就是很明显的递推问题了。

简单想就是dp[i][j][k]=c[n*m][k]-sigma(c[i][ii]*c[j][jj]*dp[ii][jj][kk]*c[(i-ii)*(j-jj)][k-kk])(ii<=i&&jj<=j&&kk<=k)

真的有这么简单吗;

对于ii来说如果没有用到第i行,那么本质上和i-1时的ii有什么区别(c[i][ii]=c[i-1][ii-1]+c[i-1][ii])

所以真正的公式应该是dp[i][j][k]=c[n*m][k]-sigma(c[i-1][ii-1]*c[j][jj]*dp[ii][jj][kk]*c[(i-ii)*(j-jj)][k-kk])(0<ii<=i&&jj<=j&&kk<=k)

当然dp[i][j][k]=c[n*m][k]-sigma(c[i][ii]*c[j-1][jj-1]*dp[ii][jj][kk]*c[(i-ii)*(j-jj)][k-kk])(ii<=i&&0<jj<=j&&kk<=k)也是可以的,但是因为ii的循环再jj外面,所以优化ii可以缩短时间,亲测优化了一倍。

代码:

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<ctime>
#include "cstdio"
#include "string"
#include "string.h"
#include "map"
using namespace std;
#define LL long long
const int mod = 1e9 + 7;
LL dp[15][15][105];
LL c[105][105];
int pow2[111];
int n, m;
int main()
{
	c[0][0] = pow2[0] = 1;
	for (int i = 1;i <= 100;i++)
	{
		pow2[i] = (pow2[i - 1] * 2) % mod;
		c[i][0] = c[i][i] = 1;
		for (int j = 1;j < i;j++)
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
	dp[0][1][0] = dp[1][0][0] = 1;
	for (int i = 1;i <= 10;i++)
	{
		for (int j = 1;j <= 10;j++)
		{
			if (i == 1 || j == 1)
			{
				dp[i][j][i + j - 1] = 1;
				continue;
			}
			for (int k = 1;k <= i*j;k++)
			{
				dp[i][j][k] = c[i*j][k];
				for (int ii = 1;ii <= i;ii++)
				{
					for (int jj = 0;jj <= j;jj++)
					{
						if (i + j == ii + jj)
							continue;
						for (int kk = 0;kk <= k;kk++)
						{
							LL temp = c[i - 1][ii - 1] * c[j][jj] % mod;
							temp = temp*dp[ii][jj][kk] % mod;
							temp = temp*c[(i - ii)*(j - jj)][k - kk];
							dp[i][j][k] = (dp[i][j][k] + mod - temp) % mod;
						}
					}
				}
			}
		}
	}
	while (scanf("%d %d", &n, &m) != EOF)
	{
		LL ans = 0;
		for (int i = 0;i <= n*m;i++)
			ans = (ans + dp[n][m][i] * pow2[i]) % mod;
		printf("%lld\n", ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值