51nod 1780 完美序列

本文介绍了一道涉及插空法和组合数学的题目解决思路。通过预处理数的个数,采用动态规划方法,利用插空技巧解决连续数对的问题。文章详细解释了状态转移方程,并给出了完整的代码实现。

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

题目
题解
先预处理出来每种大小的数的个数,并在这个过程进行判断是否连续(不大于 1),然后,我们可以从小到大进行插空法插数,那么如何插呢?假如,此时我们已经查到数 i,那么合法的插孔分为两种,第一种是插在两个 i1 i − 1 之间,另一种就是当首尾有 i1 i − 1 时,我们可以在首尾两侧进行插空。那么我们需要考虑的也就是此时考虑到了第 i i 种数、有多少个相邻的第 i i 种数对儿、首尾有几个第 i i 种数,自然最后这个状态只有三种,就是 0、1、2,所以我们设 dp[i][j][0/1/2] d p [ i ] [ j ] [ 0 / 1 / 2 ] 表示此时考虑到了第 i i 种数时,首尾第 i i 种数的个数分别为 0、1、2,相邻的第i种数对儿个数加上首尾第 i i 种数的个数的和为 j j 时的方案数。初始化,我们应该设 dp[1][cnt[1]+1][2]=1 d p [ 1 ] [ c n t [ 1 ] + 1 ] [ 2 ] = 1 ,至于为什么,很容易理解,此时,第一种数的方案只有一种,首尾第一种数的个数为2,连续的第一种数对儿为 cnt[1]1 c n t [ 1 ] − 1 ,所以,你没有看错, cnt[1]+1 c n t [ 1 ] + 1
实际上就是此时插空时允许插空的数目。
综上所述,这个题核心就是插空法,组合数学的知识,另外还要使用一次插空法的地方是,要考虑将 cnt[i] c n t [ i ] 个数划分为 k k 份时。
就这样,没有什么问题了,需要讲解的都说的十分清楚了,如果还有什么问题,可以评论区留言哦~~~

哦,对了,最后结果自然是不同插空数时三种情况的和的和喽!也就是说需要控制该 dp
的第二维的量,因为我们说明了,第二维实际上就是此时允许的插空数目。Over~~~

#include<bits/stdc++.h>
using namespace std;
const int M=1e9+7,N=30002;
typedef long long ll;
int n,i,tot,a[N],j,k,cnt[102],x;
ll f[N][102][3],c[102][102],ans,tmp;
int main(){
    scanf("%d",&n);
    for (i=0;i<102;i++){
        c[i][0]=1;
        for (j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%M;
    }
    scanf("%d",&a[1]);
    cnt[tot=1]=1;
    for (i=2;i<=n;i++){
        scanf("%d",&a[i]);
        if (a[i]-a[i-1]>1){
            puts("0");
            return 0;
        }
        if (a[i]==a[i-1]) cnt[tot]++;
        else cnt[++tot]=1;
    }
    f[1][cnt[1]+1][2]=1;
    for (i=1;i<tot;i++){
        x=cnt[i+1];
        for (j=0;j<=cnt[i]+1;j++)
            for (k=1;k<=j;k++){
                tmp=c[x-1][k-1];
                if (x-k>=0) (f[i+1][x-k][0]+=(f[i][j][0]*c[j][k]+f[i][j][1]*c[j-1][k]+f[i][j][2]*c[j-2][k])%M*tmp%M)%=M;
                if (x-k+1>=0) (f[i+1][x-k+1][1]+=(f[i][j][1]*c[j-1][k-1]+f[i][j][2]*c[j-2][k-1]*2)%M*tmp%M)%=M;
                if (x-k+2>=0 && k-2>=0) (f[i+1][x-k+2][2]+=f[i][j][2]*c[j-2][k-2]%M*tmp%M)%=M;
            }
    }
    for (i=0;i<=cnt[tot]+1;i++)
        for (j=0;j<3;j++) (ans+=f[tot][i][j])%=M;
    printf("%lld",ans);
}
题目 51nod 3478 涉及一个矩阵问题,要求通过最少的操作次数,使得矩阵中至少有 `RowCount` 行和 `ColumnCount` 列是回文的。解决这个问题的关键在于如何高效地枚举所有可能的行和列组合,并计算每种组合所需的操作次数。 ### 解法思路 1. **预处理每一行和每一列变为回文所需的最少操作次数**: - 对于每一行,计算将其变为回文所需的最少操作次数。这可以通过比较每对对称位置的值是否相同来完成。 - 对于每一列,计算将其变为回文所需的最少操作次数,方法同上。 2. **枚举所有可能的行和列组合**: - 由于 `N` 和 `M` 的最大值为 8,因此可以枚举所有可能的行组合和列组合。 - 对于每一种组合,计算其所需的最少操作次数,并取最小值。 3. **计算操作次数**: - 对于每一种组合,需要计算哪些行和列需要修改,并且注意行和列的交叉点可能会重复计算,因此需要去重。 ### 代码实现 以下是一个可能的实现方式,使用了枚举和位运算来处理组合问题: ```python def min_operations_to_palindrome(matrix, row_count, col_count): import itertools N = len(matrix) M = len(matrix[0]) # Precompute the cost to make each row a palindrome row_cost = [] for i in range(N): cost = 0 for j in range(M // 2): if matrix[i][j] != matrix[i][M - 1 - j]: cost += 1 row_cost.append(cost) # Precompute the cost to make each column a palindrome col_cost = [] for j in range(M): cost = 0 for i in range(N // 2): if matrix[i][j] != matrix[N - 1 - i][j]: cost += 1 col_cost.append(cost) min_total_cost = float('inf') # Enumerate all combinations of rows and columns rows = list(range(N)) cols = list(range(M)) from itertools import combinations for row_comb in combinations(rows, row_count): for col_comb in combinations(cols, col_count): # Calculate the cost for this combination cost = 0 # Add row costs for r in row_comb: cost += row_cost[r] # Add column costs for c in col_comb: cost += col_cost[c] # Subtract the overlapping cells for r in row_comb: for c in col_comb: # Check if this cell is part of the palindrome calculation if r < N // 2 and c < M // 2: if matrix[r][c] != matrix[r][M - 1 - c] and matrix[N - 1 - r][c] != matrix[N - 1 - r][M - 1 - c]: cost -= 1 min_total_cost = min(min_total_cost, cost) return min_total_cost # Example usage matrix = [ [0, 1, 0], [1, 0, 1], [0, 1, 0] ] row_count = 2 col_count = 2 result = min_operations_to_palindrome(matrix, row_count, col_count) print(result) ``` ### 代码说明 - **预处理成本**:首先计算每一行和每一列变为回文所需的最少操作次数。 - **枚举组合**:使用 `itertools.combinations` 枚举所有可能的行和列组合。 - **计算成本**:对于每一种组合,计算其成本,并考虑行和列交叉点的重复计算问题。 ### 复杂度分析 - **时间复杂度**:由于 `N` 和 `M` 的最大值为 8,因此枚举所有组合的时间复杂度为 $ O(N^{RowCount} \times M^{ColCount}) $,这在实际中是可接受的。 - **空间复杂度**:主要是存储预处理的成本,空间复杂度为 $ O(N + M) $。 ### 相关问题 1. 如何优化矩阵中行和列的枚举组合以减少计算时间? 2. 在计算行和列的交叉点时,如何更高效地处理重复计算的问题? 3. 如果矩阵的大小增加到更大的范围,如何调整算法以保持效率? 4. 如何处理矩阵中行和列的回文条件不同时的情况? 5. 如何扩展算法以支持更多的操作类型,例如翻转某个区域的值?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值