hunnu 11463 信封问题 (找规律||搜索)

题目链接:点击打开链接

题目大意:全错排问题,指定了某些信封装的信的对于编号,求全错排的解~~~~

刚开始拿到这个题目感觉无从下手,无法保存搜索过程的状态,遂找到对应答案相同的解的特点,打表找到了某个稀里糊涂的规律,2333

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long llt;
const int MAXN=1100;
const int MOD=1000000007;
int env[MAXN];
llt dp[MAXN][MAXN];
void init()
{
	dp[0][0]=1;dp[1][0]=0;dp[1][1]=0;dp[2][1]=1;
	for(int i=2;i<=1000;i++)
		dp[i][0]=(dp[i-1][0]+dp[i-2][0])*(i-1)%MOD;
	for(int i=4;i<=1000;i+=2) dp[i][i/2]=dp[i-2][(i-2)/2]*(i/2)%MOD;
	for(int j=1;j<=500;j++)
		for(int i=2*j+1;i<=1000;i++)
			dp[i][j]=((i-j)*dp[i-1][j]%MOD+MOD-j*dp[i-3][j-1]%MOD)%MOD;
}
int main() 
{
    int n;
    init();
    while(scanf("%d",&n)!=EOF)
    {
    	int ret=0,hook=0;
    	bool flag=false;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&env[i]);
    		if(env[i]) ret++;
    		if(env[i]==i) flag=true;
    	}
    	if(flag)
    	{
    		puts("0");
    		continue;
    	}
    	for(int i=1;i<=n;i++) 
    		if(env[env[i]]) hook++;
    	printf("%d\n",dp[n-hook][ret-hook]);
    }
    return 0;
}
后面还发现另一种容易理解的解法~~~

#include <cstdio>
#include <cstring>
using namespace std;
int a[1010];
bool f[1010];
__int64 dp[1010][1010];
const __int64 mod=1000000007LL;
__int64 dfs(int,int);
void debug()
{
    for(int i=0;i<=9;i++)
    {
        for(int j=0;j<=i/2;j++)
          printf("DP[%d][%d] :%-4d ",i,j,dfs(i,j));
        putchar('\n');
    }
}
//a个不能放在其原本位置,b个可以放在任意位置
__int64 dfs(int a,int b)
{
    if(a<0||b<0)
        return 0;
    if(dp[a][b]>=0)
        return dp[a][b];
    __int64 ret;
    //如果b的数量为0,就变成了全错排问题
    if(b==0) ret=(a-1)*(dfs(a-1,0)+dfs(a-2,0));
    //else ret=dfs(a+1,b-1)+dfs(a,b-1);//玄学问题,这个也能过,不过过程不理解
    else ret=a*dfs(a-1,b)+b*dfs(a,b-1);
    ret%=mod;
    return dp[a][b]=ret;
}
int main()
{
    int i,n,cnta,cntb;
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    bool flag;
    memset(dp,-1,sizeof(dp));
    dp[0][0]=1;
    debug();
    while(scanf("%d",&n)!=EOF)
    {
          memset(f,0,sizeof(f));
          for(i=1;i<=n;i++)
               scanf("%d",&a[i]);
          cnta=cntb=0;
          flag=1;
          for(i=1;i<=n;i++)
            if(a[i]!=0)
            {
               if(f[a[i]]==1||a[i]==i) flag=0;
               f[a[i]]=1;
            }
          for(i=1;i<=n;i++)
               if(a[i]==0)
               {
                    if(f[i]==0) cnta++;
                    else  cntb++;
               }
          ///cnta表示不能放在其原本位置的信封的个数
          ///cntb表示可以随意放置的信封数量
          if(!flag) printf("0\n");
          else printf("%I64d\n",dfs(cnta,cntb));
    }
    return 0;
}


(最近写题解越来越不走心了,233333333,如果有HUNNU的小伙伴在有生之年能看到我这篇题解,看不懂就直接来问我吧,233333333~~~~~)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值