题解 - P6603 甜梦

题解 - P 6603 \mathrm{P6603} P6603甜梦

题目意思

S o l \mathrm{Sol} Sol

  • 一道很神仙的状压 D P DP DP

  • 我们发现 l ≤ 12 l\leq 12 l12,我们自然而然地想到了状压。于是 f S , u f_{S,u} fS,u表示在状态 S S S下较小的点为 u u u的最大快乐值。 S S S这个东西其实是对 [ u , u + l ] [u,u+l] [u,u+l]这段区间的二进制表示。

  • 我们如何用较小 u u u标号去推出较大标号 v v v,我们首先说出结论 v = u + H i g h ( S ) v=u+High(S) v=u+High(S)。其中 H i g h ( S ) High(S) High(S) S S S状态下最高位 1 1 1的位置。具体是因为我们的 D A G DAG DAG是走不了回头路的,所以 v v v一定是当前状态下的最高位。这个还是可以理解的吧。

  • 接下来我们来看转移,其实莫过于 3 3 3种情况。

  • [ 1 ] [1] [1] u , v u,v u,v同时移动到 P P P

    • 那么转移: f 1 , P = max ⁡ ( f 1 , P , f S , u + v a l P ) f_{1,P}=\max(f_{1,P},f_{S,u}+val_{P}) f1,P=max(f1,P,fS,u+valP)
    • 这个好理解就是两个点并到一个点,然后更新状态为 1 ( 2 0 ) 1(2^0) 1(20)
  • [ 2 ] [2] [2] v v v移动到 P P P u u u不动

    • 那么转移: f S ∣ ( 1 < < P − u ) , u = max ⁡ ( f S ∣ ( 1 < < P − u ) , u , f S , u + v a l P f_{S|(1<<P-u),u}=\max(f_{S|(1<<P-u),u},f_{S,u}+val_P fS(1<<Pu),u=max(fS(1<<Pu),u,fS,u+valP

    • 这个不需要考虑以前状态是否出现过,因为编号是递增的

  • [ 3 ]   u [3]\ u [3] u移动到 P P P v v v不动

    • 如果此时 P > v P>v P>v那么 v v v成为较小点,于是 f ( S > > v − u ) ∣ ( 1 < < P − v ) , v = max ⁡ ( f ( S > > v − u ) ∣ ( 1 < < P − v ) , v , f S , u + v a l P ) f_{(S>>v-u)|(1<<P-v),v}=\max(f_{(S>>v-u)|(1<<P-v),v},f_{S,u}+val_{P}) f(S>>vu)(1<<Pv),v=max(f(S>>vu)(1<<Pv),v,fS,u+valP)

    • 此时这个 v a l P val_P valP要看在上一个状态中是否出现过

    • 如果此时 P < v P<v P<v那么 P P P还是较小点,于是 f S > > P − u ∣ 1 , u = max ⁡ ( f S > > P − u ∣ 1 , u , f S , u + v a l P ) f_{S>>P-u|1,u}=\max(f_{S>>P-u|1,u},f_{S,u}+val_{P}) fS>>Pu1,u=max(fS>>Pu1,u,fS,u+valP)

    • 此时这个 v a l P val_P valP要看在上一个状态中是否出现过

  • 于是答案就是 f 1 , n f_{1,n} f1,n。就是两个点都在 n n n点,其他就是写细节问题啦~~~

C o d e \mathrm{Code} Code

#include <bits/stdc++.h>
#define For(i,a,b) for ( int i=(a);i<=(b);i++ )
#define Dow(i,b,a) for ( int i=(b);i>=(a);i-- )
#define GO(i,x) for ( int i=head[x];i;i=e[i].nex )
#define mem(x,s) memset(x,s,sizeof(x))
#define cpy(x,s) memcpy(x,s,sizeof(x))
#define YES return puts("YES"),0
#define NO return puts("NO"),0
#define GG return puts("-1"),0
#define pb push_back
using namespace std;

inline int read()
{
	int sum=0,ff=1; char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=getchar();
	return sum*ff;
}

const int mod=1e9+7;
const int mo=998244353;
const int N=5005;
const int M=1<<13|1;

int n,m,l,val[N],a[N][N],f[N][M],High[M];
vector<int> G[N];

int main()
{
	n=read();m=read();l=read();
	For(i,1,n) val[i]=read();
	For(i,1,m)
	{
		int x,y;
		x=read(),y=read();
		if(!a[x][y]) a[x][y]=1,G[x].pb(y);
	}
	For(S,0,(1<<l+1)-1) for ( int j=l+1;~j;j-- ) if((S>>j)&1)
	{
		High[S]=j;
		break;
	}
	memset(f,-1,sizeof(f));
	For(i,1,n)
	{
		if(f[i][1]==-1) f[i][1]=0;
		For(S,0,(1<<l+1)-1)
		{
			if(!(S&1)) continue;
			int idv=i+High[S];
			if(f[i][S]==-1) continue;
			For(j,0,(int)G[idv].size()-1)
			{
				int v=G[idv][j];
				if(a[i][v]) f[v][1]=max(f[i][S]+val[v],f[v][1]);
				if(v-i>l) continue;
				int nxt=S|(1<<v-i);
				f[i][nxt]=max(f[i][nxt],f[i][S]+val[v]);
			}
			For(j,0,(int)G[i].size()-1)
			{
				int v=G[i][j];
				if(v-idv>l) continue;//1:
				if(v>idv)
				{
					int nxt=(S>>idv-i)|(1<<v-idv);
					if(S>>v-i&1) f[idv][nxt]=max(f[idv][nxt],f[i][S]);
					else f[idv][nxt]=max(f[idv][nxt],f[i][S]+val[v]);
				}
				else 
				{
					int nxt=S>>v-i|1;
					if(S>>v-i&1) f[v][nxt]=max(f[v][nxt],f[i][S]);
					else f[v][nxt]=max(f[v][nxt],f[i][S]+val[v]);
				}
			}
		}
	}
	printf("%d\n",f[n][1]);
	return 0;
}
				
		
		
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值