HDU 4783 Clumsy Algorithm

题意不提。

  我们可以发现,可以将最终序列分为对于第i个位置i-pi>=0与i-pi<0种两个子序列。且如果f[n]==g[n],则有两个子序列都递增。

  原因是f[n]表示1-n这个排列的逆序对个数,即冒泡排序的交换次数,而每个g[i]表示将p[i]从i位置移到它应当在的p[i]位置的交换次数。

  考虑将每个满足i-p[i]>0的p[i]从i位置移到p[i]位置是正确的条件,显然对于i-p[i]>0的每个p[i]必须递增,否则,会产生p[i]与p[j]交换时的交叉,使冒泡的代价增大。

  若 i-p[i]<0 的p[i]不递增,它们之间会产生新的冒泡,使冒泡的代价增加。

  所以就是DP了,设f[i][j]表示已放了j个数,其中最大数为i的且满足限制的方案数,显然如果j+1的位置放i-p[i]<0的,直接枚举i+1-n的数字即可。

  若j+1的位置放i-p[i]>=0的数字,由于i-p[i]>=0的数字必须递增,且i递增,因此有一个必选的数字直接填入即可。

  直接转移即可。

  

#include<bits/stdc++.h>
#define MOD 1000000007
using namespace std;
#define FILE "chad"
set<int> S;
int n, k, f[105][105];

int main()
{
	//freopen(FILE".in","r",stdin);
	//freopen(FILE".out","w",stdout);
	int T; scanf("%d",&T);
	for(int tt = 0;T--;)
	{
		memset(f, 0, sizeof f);
		S.clear();
	
		scanf("%d%d",&n,&k);
		for(int i = 1; i <= n; i++) S.insert(i);
		int mx = 0, flag = 1;
		for(int i = 1; i <= k; i++)
		{
			int x; scanf("%d",&x);
			if(x > mx)
			{
				mx = x;
				
			}
			else if(x != *S.begin()) flag = 0;
			S.erase(x);
		}
		f[mx][k] = flag;
		for(int i = 0; i <= n; i++)
		{
			for(int j = 0; j < n; j++)
			{
				if(i-j > 0) (f[i][j+1] += f[i][j]) %= MOD;
				for(int k = i+1; k <= n; k++)
					(f[k][j+1] += f[i][j]) %= MOD;
			}
		}
		printf("Case #%d: %d\n",++tt,f[n][n]);
		
	}
}

//代码来自某AC代码,侵删。

  

posted @ 2018-07-14 23:18  CHADLZX  阅读(286)  评论(0编辑  收藏  举报