1. 程式人生 > 其它 >F. Mattress Run 題解

F. Mattress Run 題解

F. Mattress Run

挺好的一道題,對於DP的本質的理解有很大的幫助。
首先要想到的就是將這個拆成兩個題,一個dp光求獲得足夠的夜晚的最小代價,一個dp光求獲得足夠的停留的最小代價。
顯然由於這個題需要儲存的資訊很大,我們設狀態時就要思考如何才能在規定的時間內完成而不超時。
請求有5000個,若設與請求有關的狀態,例如:我們設f[i][j],表示當前在請求i,獲得的資格j的最小代價,確實,因為我們知道了請求i的所有資訊,包括結束時間,所在的旅館號的資訊,但在狀態轉移時,難免要列舉上一次的請求所在的位置,50005000,別忘了還有j的列舉再50,這樣就爆了,別報僥倖心理,這個題輸出方案加上你dp要做兩邊,常數巨大。那麼考慮重新設計狀態,考慮到轉移時,實際上若上一次的天數不為a[i].l,我們根本不用在意他在哪個旅館,當上一次的天數恰好為a[i].l時,我們才考慮他的旅館。那麼我們可以將天數和旅館都設在狀態裡,設f[i][j][k]表示在第i天在旅館j獲得的資格為k的最小代價。先列舉請求,然後考慮每個請求會使得哪些狀態發生改變,狀態轉移時,為了優化,我們令設dp[i][j]表示在第i天獲得資格j的最小代價,方便我們省去列舉的旅館數,之後我們另外討論天數為a[i].l的情況即可。複雜度為5000365

50,輕鬆跑過,輸出方案的話,我們大可用結構體來記錄當前狀態由哪一個轉態得來。之後逆推一下即可。

#include<bits/stdc++.h>
using namespace std;
int n,s[2],h,m,c[5005],num;
int f[370][52][52][2],dp[370][52][2];
struct ss{int x,y,z;}Z[370][52][52][2],S[370][52][2];
//設f[i][j][k]表示第i天在j號旅館獲得的資格為k的最小代價,
//dp[i][j]表示第i天獲得的資格為j的最小代價。 
struct wy{int l,r,val,id,qid;}a[5005];
inline bool cmp(wy a,wy b)
{
	return a.l<b.l;
}
inline void solve(int op)
{
	f[0][0][0][op]=0;dp[0][0][op]=0;
	for(int i=1;i<=m;++i)//列舉所有的請求。 
	{//轉移來的天數分為兩類,一類小於l,一類等於l 
		for(int j=0;j<a[i].l;++j)//列舉所有可以改變的狀態的天數
			for(int k=0;k<=s[op];++k)
			{
				int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]);
				if(dp[j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op])
				{
					f[a[i].r][a[i].id][d][op]=dp[j][k][op]+a[i].val;
					Z[a[i].r][a[i].id][d][op]=S[j][k][op];
				}
				if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op])
				{
					dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op];
					S[a[i].r][d][op]={a[i].r,a[i].id,d};
				}
			} 
		for(int j=0;j<=h;++j)	//等於l的,列舉從那個旅館過來	
		{
			if(j==a[i].id) continue;
			for(int k=0;k<=s[op];++k)	//列舉上一次的狀態。
			{
				int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]);
				if(f[a[i].l][j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op])
				{
					f[a[i].r][a[i].id][d][op]=f[a[i].l][j][k][op]+a[i].val;
					Z[a[i].r][a[i].id][d][op]=(ss){a[i].l,j,k};
				}
				if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op])
				{
					dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op];
					S[a[i].r][d][op]={a[i].r,a[i].id,d};
				}
			} 
		}
	}
}
inline void work(ss x,int op)
{
	if(x.x==0||x.y==0||x.z==0) return;
	for(int i=1;i<=m;++i)
	{
		if(a[i].r==x.x&&a[i].id==x.y)
		{
			ss k=Z[x.x][x.y][x.z][op];
			int d=min((op==0?k.z+a[i].r-a[i].l:k.z+1),s[op]);
			if(d==x.z&&f[x.x][x.y][x.z][op]-a[i].val==f[k.x][k.y][k.z][op])
			{
				c[++num]=a[i].qid;
				work(k,op);
				return;
			}
		}
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	scanf("%d%d%d%d%d",&n,&s[0],&s[1],&h,&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d%d",&a[i].id,&a[i].l,&a[i].r,&a[i].val);
		a[i].qid=i;
	}
	sort(a+1,a+m+1,cmp);
	memset(f,0x3f,sizeof(f));
	memset(dp,0x3f,sizeof(dp));
	solve(0);solve(1);
	int ans1=1e9,ans2=1e9,o1,o2;
	for(int i=0;i<=n;++i)
	{
		if(dp[i][s[0]][0]<ans1)
		{
			ans1=dp[i][s[0]][0];
			o1=i;
		} 
		if(dp[i][s[1]][1]<ans2)
		{
			ans2=dp[i][s[1]][1];
			o2=i;
		}
	}
	if(min(ans1,ans2)==1e9) {puts("IMPOSSIBLE");return 0;}
	else if(ans1<=ans2) work(S[o1][s[0]][0],0),puts("NIGHTS");
	else if(ans1>ans2)  work(S[o2][s[1]][1],1),puts("STAYS");
	printf("%d\n",num);
	for(int i=num;i>=1;--i) printf("%d ",c[i]);
	return 0;
} 

人一旦有了自信,就擁有了一切!