• 【AGC030F】Permutation and Minimum(DP)


    题目链接

    题解

    首先可以想到分组后,去掉两边都填了数的组。
    然后就会剩下((-1,-1))((-1,x))((x,-1))这两种情况
    因为是最小值序列的情况数,我们可以考虑从大到小填数,一个组填完了最小值就是当前填的数
    (f[i][j][k])表示已经填到数(i)剩余(j+k)个填了一个数的组,其中有(j)个组是第一种情况填出来的,(k)个是第二种情况来的
    分情况转移
    如果这个数属于第一种情况,那么可以转移到:
    (f[i][j+1][k]:)填到一个((-1,-1))
    (f[i][j-1][k]:)和之前填了一个数的((-1,-1))完成一组
    (f[i][j][k-1]:)((x,-1))完成一组
    如果属于第二种情况,那么可以转移到:
    (f[i][j][k+1]:)不是最小值
    (f[i][j-1][k]:)是最小值
    最后因为((-1,-1))是无序的,所以答案要乘上一个阶乘

    Code

    int cnt1 = 0, cnt2 = 0;
    	for (int i = 1; i < 2 * n; i += 2) {
    		if (a[i] == a[i + 1])
    			cnt2++;
    		else {
    			if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
    			if (a[i] == -1) cnt1++, flg[a[i + 1]] = 1;			
    			else vis[a[i]] = vis[a[i + 1]] = 1;
    		}
    	}
    	int m = 0;
    	f[0][0][0] = 1;
    	for (int i = 2 * n; i >= 1; i--)
    		if (!vis[i]) {
    			m++;
    			if (!flg[i]) {
    				for (int j = 0; j <= cnt1 + cnt2; j++)
    					for (int k = 0; k <= cnt1; k++) {
    						pls(f[m][j + 1][k], f[m - 1][j][k]);
    						if (j) pls(f[m][j - 1][k], f[m - 1][j][k]);
    						if (k) pls(f[m][j][k - 1], 1ll * k * f[m - 1][j][k] % Mod);
    					}
    			}
    			else {
    				for (int j = 0; j <= cnt1 + cnt2; j++)
    					for (int k = 0; k <= cnt1; k++) {
    						pls(f[m][j][k + 1], f[m - 1][j][k]);
    						if (j) pls(f[m][j - 1][k], f[m - 1][j][k]);
    					}
    			}
    		}
    	int ans = f[m][0][0];
    	for (int i = 1; i <= cnt2; i++)
    		ans = 1ll * ans * i % Mod;
    
  • 相关阅读:
    day5模块
    day5时间复杂度
    day5冒泡排序
    day4正则表达式
    C语言 经典编程100题
    C语言 第八章 函数、指针与宏
    C语言 第七章 数组与字符串
    C语言 第六章 多重循环
    C语言 第五章 循环结构
    C语言 第四章 分支结构练习
  • 原文地址:https://www.cnblogs.com/zzy2005/p/12005563.html
Copyright © 2020-2023  润新知