首页 > 技术文章 > [题解] P7154 [USACO20DEC] Sleeping Cows P

Liang-sheng 2021-08-20 11:51 原文

[题解] P7154 [USACO20DEC] Sleeping Cows P

点此看题

不一定更好的阅读体验

解题报告

\(n\) 头奶牛和 \(n\) 个牛棚,一个奶牛 \(u_i\) 能进入一个牛棚 \(v_i\) 当且仅当 \(s_{u_i}\leq t_{v_i}\),一个牛棚最多只能容纳一头牛。

定义一个完美匹配为:对于所有未分配的奶牛,都不能有任何一个未匹配的牛棚满足条件,求完美匹配的方案数。

关键是找到那些没有匹配的牛棚来计算。

考虑把 \(s\)\(t\) 都放到一起排序,这样做的好处就是:对于一个牛棚,如果前面有不需要匹配的牛,那么这个牛棚必须匹配一头牛。

于是设计状态为 \(f[i,j,0/1]\) 表示当前考虑了前 \(i\) 个元素,有 \(j\) 头牛准备被分配到牛棚里但是还没有分配\(0/1\) 表示有没有丢掉牛(不匹配的牛),\(0\) 表示有,\(1\) 表示没有。

注意:\(j\) 和第三维的 \(0/1\) 没有任何关系

初始为 \(f[0][0][1]=1\),因为任何牛都没有丢掉。

状态转移

设考虑到了第 \(i\) 个位置。

  • \(i+1\) 位置为牛。

\(f[i,j,0]->f[i+1,j+1,0],f[i+1,j,0]\)

\(f[i,j,1]->f[i+1,j+1,1],f[i+1,j,0]\)​​

分为下一轮丢没丢奶牛进行考虑。

  • \(i+1\) 个位置为牛棚。

\(j\times f[i,j,0]->f[i+1,j-1,0]\)

\(j\times f[i,j,1]->f[i+1,j-1,1]\)

\(f[i,j,1]->f[i+1,j,1]\)

前两种情况是考虑把当前需要分配的 \(j\) 头牛分配进去。

第三种情况是这个牛棚直接作废。

注意:如果当前丢掉了一些奶牛,那么这个牛棚必须被填满,所以 \(f[i,j,0]\) 是无法转移到 \(f[i+1,j,0]\) 的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
    T x=0;char ch=getchar();bool fl=false;
    while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
    while(isdigit(ch)){
        x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
    }
  	return fl?-x:x;
}
#define LL long long
const int maxn = 3000 + 10;
const int P = 1e9 + 7;
struct node{
	int val,type;
	bool operator <(const node &x)const{
		if(val!=x.val)return val<x.val;
		return type<x.type;
	}
}a[maxn<<1];
int s[maxn],t[maxn],n;
int f[maxn<<1][maxn][2],ans;
inline void add(int &a,int b){
	a+=b;
	if(a>=P)a-=P;
}
#define read() read<int>()
int main(){
	n=read();
	for(int i=1;i<=n;i++)s[i]=read(),a[i]=(node){s[i],0};
	for(int i=1;i<=n;i++)t[i]=read(),a[i+n]=(node){t[i],1};
	sort(a+1,a+1+n*2);
	f[0][0][1]=1;
	for(int i=0;i<n*2;i++){
		if(a[i+1].type==0){
			for(int j=0;j<=n+1;j++){
				add(f[i+1][j+1][0],f[i][j][0]);
				add(f[i+1][j][0],f[i][j][0]);
				add(f[i+1][j+1][1],f[i][j][1]);
				add(f[i+1][j][0],f[i][j][1]);
			}
		}
		else {
			for(int j=0;j<=n+1;j++){
				if(j>0)add(f[i+1][j-1][0],1LL*j*f[i][j][0]%P);
				if(j>0)add(f[i+1][j-1][1],1LL*j*f[i][j][1]%P);
				add(f[i+1][j][1],f[i][j][1]);
			}
		}
	}
	ans=f[n*2][0][0]+f[n*2][0][1];ans%=P;
	printf("%d\n",ans);
	return 0;
}

小结

核心是放在一起排序DP,保证了合法状态方便转移

推荐阅读