1. 程式人生 > 其它 >[題解] P7154 [USACO20DEC] Sleeping Cows P

[題解] P7154 [USACO20DEC] Sleeping Cows P

[題解] 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,保證了合法狀態方便轉移