• 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的情况即可。复杂度为500036550,轻松跑过,输出方案的话,我们大可用结构体来记录当前状态由哪一个转态得来。之后逆推一下即可。

    #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
    ",num);
    	for(int i=num;i>=1;--i) printf("%d ",c[i]);
    	return 0;
    } 
    

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

  • 相关阅读:
    分布式系统之CAP理论杂记
    RPC详解
    玩转zookeeper命令
    NRPE介绍
    开启irqbalance提升服务器性能
    xinetd被动服务唤醒
    服务发现的基本原理[转]
    关于TCP/IP,必知必会的十个经典问题[转]
    Smart Client技术简要总结
    使用ng-grid实现可配置的表格
  • 原文地址:https://www.cnblogs.com/gcfer/p/15363628.html
Copyright © 2020-2023  润新知