Tri Tiling+递推

探讨使用2x1瓷砖填充3xn矩形的所有可能方式。通过递归划分方法,结合数学归纳法推导出高效的动态规划解决方案。

 

Tri Tiling

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1453    Accepted Submission(s): 828


Problem Description
In how many ways can you tile a 3xn rectangle with 2x1 dominoes? Here is a sample tiling of a 3x12 rectangle.


 


 

Input
Input consists of several test cases followed by a line containing -1. Each test case is a line containing an integer 0 ≤ n ≤ 30.
 


 

Output
For each test case, output one integer number giving the number of possible tilings.
 


 

Sample Input
2 8 12 -1
 


 

Sample Output
3 153 2131

 

 

/*
思路:
如果n为奇数,必然无解。

当n为偶数时,一个比较直观的思路就是把大矩形用竖线切出左边一部分,然后递归求解,就像UVA_10359那样,
直接考虑最左边一个小矩形是如何构成的我们就可以递归得到f(n)递推式。

虽然这个题目乍看上去小矩形似乎拼法比较多,而且要拼出一个不能再用竖线切分的小矩形似乎拼的方法也并不直观。
但如果我们在纸上多画一画的话,对于拼任意一个横向边长为x的不可再用竖线切分的小矩形,如果x为2,显然有3种拼法,
如果x为大于2的偶数,那么只有2种拼法。

至于如果x>2为什么只有两种拼法,我们不妨实际拼一下。首先第一列肯定只有两种情况,第一种是一个横的在上面
,然后一个竖的在左下方,第二种是一个横的在下面,然后一个竖的在左上方,因为两种情况对称,我们只讨论第一种情况。

现在已经拼好两个了,如果横的下面再放一个竖的,那么显然这个就成了一个x=2的小矩形了,
那么最后拼出来的就不符合我们前面说的不可再用竖线切分的特征了,因此横的下面也即竖的右边,只能再放两个横的,
放完两个横的我们就发现两个横的上面有一个小正方形区域,这个区域只能楔进去一个横的,等把这个横的画完之后,My God,
我们会发现一个惊人的事实,现在的这个结构是和我们最初放完一个横的一个竖的的那种情况的结构是一样的!因此,
如果我们想继续向右拼出不能被竖线切分的矩形,那么只能是重复之前的操作,因而得到的最后横向边长为x的矩形是唯一确定的。

前面我们最初拼的时候只取了对称的两种情况之一,因此,如果x>2,拼出一个不能被竖线切割的矩形的方法只有两种。

那么递推公式自然就有了,f(n)=3*f(n-2)+2*f(n-4)+…+2*f(0),
然后再写出f(n-2)的递推式后两式作差就可以得到f(n)=4*f(n-2)-f(n-4),
递归的边界是f(0)=1,f(2)=4。


*/

#include<iostream>
using namespace std;
int dp[32];
void pre()
{
		
		memset(dp,0,sizeof(dp));
		dp[0]=1;
		dp[2]=3;
		for(int i=4;i<=30;i++)
			dp[i]=4*dp[i-2]-dp[i-4];
}
int main()
{
	int n;
	pre();
	while(cin>>n&&n!=-1){
		cout<<dp[n]<<endl;
	}
	return 0;
}


 

<think>我们正在解决Android OpenCV应用中轮廓无法显示的问题。根据用户描述,代码中涉及MatOfPoint2f、Imgproc.contourArea和Imgproc.approxPolyDP等操作。 可能的原因包括: 1. 图像预处理不当,导致轮廓检测失败。 2. 轮廓过滤条件(如面积、顶点数)设置不合理,导致有效轮廓被过滤。 3. 绘制轮廓时使用了错误的Mat对象(例如在灰度图上绘制轮廓但显示的是彩色图,或者反之)。 4. 绘制轮廓的函数参数使用错误(如颜色、线宽等)。 参考引用内容: [^1]提到了图像预处理(灰度化、二值化、边缘检测)的重要性。 [^2]提供了傅里叶变换去除摩尔纹的示例(虽然不直接相关,但说明预处理的重要性)。 [^3]中的流程图指出预处理是OCR的关键步骤。 根据以上信息,我们逐步解决: 步骤1:确保预处理正确 - 将图像转换为灰度图:Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY); - 进行二值化(例如使用阈值或自适应阈值):Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); - 边缘检测(可选):Imgproc.Canny(binary, edges, threshold1, threshold2); 步骤2:轮廓检测 - 使用Imgproc.findContours()函数,注意在OpenCV不同版本中该函数的参数有差异(如返回轮廓的列表结构)。 步骤3:轮廓过滤 - 使用Imgproc.contourArea(contour)计算轮廓面积,然后根据面积小过滤。 - 使用Imgproc.approxPolyDP()进行多边形逼近,然后根据顶点数过滤(例如,车牌轮廓通常是四边形或特定多边形)。 步骤4:绘制轮廓 - 确保在正确的Mat上绘制(通常我们会在原始图像的拷贝上绘制彩色轮廓)。 - 使用Imgproc.drawContours()函数,注意参数设置(特别是轮廓索引、颜色、线宽)。 步骤5:显示图像 - 使用OpenCV的Highgui.imshow()或Android的ImageView显示图像。 常见错误: 1. 在查找轮廓时,注意Mat的类型(必须是8位单通道二值图像)。 2. 轮廓过滤条件过于严格,导致没有轮廓被绘制。 3. 绘制轮廓时,目标Mat与原始图像通道数不一致(例如,在灰度图上绘制轮廓,但显示时当作彩色图)。 示例代码框架: ```java // 1. 读入图像(如果是Android,可能是从相机或图片获取) Mat src = ...; // 原始彩色图像 Mat gray = new Mat(); Mat binary = new Mat(); // 2. 预处理:转灰度、二值化 Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY); Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); // 3. 查找轮廓 List<MatOfPoint> contours = new ArrayList<>(); Mat hierarchy = new Mat(); // 注意:不同OpenCV版本,参数可能不同(这里以4.x为例) Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); // 4. 过滤轮廓并绘制 Mat dst = src.clone(); // 在原始图像的拷贝上绘制 for (int i = 0; i < contours.size(); i++) { MatOfPoint contour = contours.get(i); // 计算轮廓面积 double area = Imgproc.contourArea(contour); // 面积过滤 if (area < 100) continue; // 多边形逼近 MatOfPoint2f contour2f = new MatOfPoint2f(contour.toArray()); MatOfPoint2f approxContour = new MatOfPoint2f(); double epsilon = 0.02 * Imgproc.arcLength(contour2f, true); Imgproc.approxPolyDP(contour2f, approxContour, epsilon, true); // 顶点数过滤(例如,我们只绘制四边形) if (approxContour.rows() != 4) continue; // 转换为MatOfPoint用于绘制 MatOfPoint points = new MatOfPoint(approxContour.toArray()); // 绘制轮廓(注意:这里绘制的是逼近后的多边形) Imgproc.drawContours(dst, Arrays.asList(points), -1, new Scalar(0, 255, 0), 2); } // 5. 显示结果(在Android中,需要将Mat转换为Bitmap并显示在ImageView) // 例如:Utils.matToBitmap(dst, displayBitmap); 然后设置给ImageView ``` 如果轮廓仍未显示,请检查: a) 预处理步骤是否生成了合适的二值图像(确保目标轮廓是白色,背景是黑色)? b) 轮廓过滤条件(面积、顶点数)是否设置过严? c) 绘制轮廓时是否使用了正确的颜色(Scalar(0,255,0)表示绿色)和线宽(2)? d) 在Android中,是否将处理后的Mat正确转换并显示在ImageView? 此外,建议使用调试工具查看中间图像(如二值图像)以确认轮廓检测步骤是否成功。 如果问题仍然存在,可以提供更多代码细节以便进一步分析。 </think>在 Android OpenCV 中解决轮廓无法显示的问题,通常由以下原因导致。结合您代码中使用的 `MatOfPoint2f`、`Imgproc.contourArea` 和 `Imgproc.approxPolyDP` 等关键函数,以下是系统性的解决方案: --- ### **关键原因与解决方案** #### 1. **预处理问题**(最常见) ```java // 确保正确的预处理流程(灰度化+二值化) Mat gray = new Mat(); Mat binary = new Mat(); Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_BGR2GRAY); // 转灰度 Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); // 二值化 ``` - **原因**:轮廓检测需要清晰的二值图像。未正确灰度化/二值化会导致轮廓提取失败[^1]。 - **验证**:输出二值图像到 `ImageView`,确认目标区域为白色(255),背景为黑色(0)。 --- #### 2. **轮廓过滤条件错误** ```java List<MatOfPoint> contours = new ArrayList<>(); Mat hierarchy = new Mat(); Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); for (MatOfPoint contour : contours) { // 面积过滤(示例) double area = Imgproc.contourArea(contour); if (area < 100) continue; // 过滤过小轮廓 // 多边形逼近 MatOfPoint2f approxCurve = new MatOfPoint2f(); MatOfPoint2f contour2f = new MatOfPoint2f(contour.toArray()); double epsilon = 0.02 * Imgproc.arcLength(contour2f, true); Imgproc.approxPolyDP(contour2f, approxCurve, epsilon, true); // 顶点数过滤(车牌通常是四边形) if (approxCurve.toArray().length != 4) continue; // 绘制有效轮廓(❗️确保此部分执行) Imgproc.drawContours(resultMat, Arrays.asList(contour), -1, new Scalar(0, 255, 0), 2); } ``` - **关键点**: - `Imgproc.contourArea` 过滤值需根据图像尺寸调整(如车牌轮廓通常在 1000-5000 像素)。 - `Imgproc.approxPolyDP` 的 `epsilon` 参数(逼近精度)过可能导致轮廓变形,建议设为轮廓周长的 0.01-0.05 倍。 - 顶点数过滤需结合实际场景(如车牌检测通常要求 4 个顶点)。 --- #### 3. **绘制目标错误** ```java // ✅ 正确:在原始图像的拷贝上绘制 Mat drawMat = srcMat.clone(); Imgproc.drawContours(drawMat, contours, -1, new Scalar(0,255,0), 2); // ❌ 错误:在二值图上绘制(显示全黑) Imgproc.drawContours(binary, contours, -1, new Scalar(255), 2); ``` - **原因**:轮廓必须绘制在 **BGR 彩色图**(非灰度/二值图)才能肉眼可见。 - **解决方案**: - 始终在原始图像的 `clone()` 上绘制。 - 使用 `Scalar(0,255,0)`(绿色)等彩色值。 --- #### 4. **OpenCV 初始化问题** ```java // 检查OpenCV库加载(AndroidManifest.xml需配置) if (!OpenCVLoader.initDebug()) { Log.e("OpenCV", "初始化失败"); } else { Log.d("OpenCV", "初始化成功"); } ``` - **调试**:添加日志确认 `OpenCVLoader.initDebug()` 返回 `true`。 --- #### 5. **图像显示问题** ```java // 将绘制后的Mat转为Bitmap显示 Bitmap outputBitmap = Bitmap.createBitmap(drawMat.cols(), drawMat.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(drawMat, outputBitmap); imageView.setImageBitmap(outputBitmap); ``` - **验证**:检查 `Bitmap` 转换是否正确,避免通道数不匹配(如灰度图转 `ARGB_8888` 需先转 BGR)。 --- ### **调试建议** 1. **可视化中间结果**:依次输出灰度图、二值图、轮廓绘制图到 `ImageView`。 2. **日志输出**:记录轮廓数量、面积和逼近后的顶点数: ```java Log.d("Contours", "Found: " + contours.size() + ", Valid: " + validCount); ``` 3. **简化测试**:用标准图像(如清晰的车牌图)替代摄像头输入,排除图像质量问题。 > 通过以上步骤,90%以上的轮廓显示问题可定位解决。若仍失败,请提供预处理后的二值图像样例和日志输出进一步分析[^1]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值