其他分享
首页 > 其他分享> > 《简单DP》

《简单DP》

作者:互联网

简单DP

「JOISC 2020 Day4」治疗计划

首先考虑问题的维度\([治疗状态],[时间],[花费]\)

治疗状态肯定无法省去

时间其实可以忽略,因为最终的答案的可行性与时间无关,但可能会加大转移的难度

事实上时间是可以不入状态的

考虑用01串表示治疗状态,但其实可以直接用前缀治疗状态

设\(dp[i]\)表示\([1,i]\)已治好的最小花费

\(111100111\),类似的情况,因为是连续的区间,所以不会出现》

转换一下,设\(dp[i]\)表示第\(i\)个计划实施后\([1,r_i]\)已被治疗

\(dp[i]=dp[j]+c[i],(r_j-l_i+1\geq|t_i-t_j|)\)

解释一下,首先对于\(i\)找到一个\(j\)使得\([1,r_i]\)被覆盖,要保证\([1,r_j]\)被感染到\(l_i\)之前被及时治疗

最初我觉得有可能有第三个计划将\(l_i-1\)及时覆盖使得dp的条件放宽

但事实上这种方案可以归纳到第三个计划的更新

这样做是\(O(n^2)\)的

考虑优化,每一\(dp_i\)是由当前满足条件最小\(dp_j\)更新

类似与\(dijkstra\),可以用当前最小更新,模型为无向图,没有顺序,因为尽管线性\(dp\)按\(r_i\)排序,但\(r_i\)大的一定不会更新小的

将上式绝对值裁开,可以得到两个不等式

\[r_j-t_j+1\geq l_i-t_i(t_i<t_j) \\ r_j+t_j+1\geq l_i+t_i(t_i\geq t_j) \]

用线段树维护最小,以\(t_i\)排序建树,树上查找后设为无穷,复杂度均摊\(O(nlog_2(n))\)

#include<bits/stdc++.h>
#define int long long
#define INF 1e14
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXM=1e5+5;
int n,m;
struct Task{
	int t,l,r,c;
	bool operator<(const Task x)const{
		return t<x.t;
	}
}a[MAXM];
int dp[MAXM];
struct node{
	int val,u;
	bool operator<(const node x)const{
		return val>x.val;
	}
}; 
priority_queue<node>q;
struct Seg{
	int l,r;
	int date1;
	int date2;
}Tree[MAXM*4];
void push_up(int p)
{
	Tree[p].date1=min(Tree[ls].date1,Tree[rs].date1);
	Tree[p].date2=min(Tree[ls].date2,Tree[rs].date2);
}
void Build(int p,int l,int r)
{
	Tree[p].l=l;
	Tree[p].r=r;
	if(l==r)
	{
		Tree[p].date1=a[l].l+a[l].t;
		Tree[p].date2=a[l].l-a[l].t;
		return;
	}
	int mid=(l+r)>>1;
	Build(ls,l,mid);
	Build(rs,mid+1,r);
	push_up(p);
}
void Update(int p,int k)
{
	if(Tree[p].l==Tree[p].r)
	{
		Tree[p].date1=INF;
		Tree[p].date2=INF;
		return;
	 } 
	 int mid=(Tree[p].l+Tree[p].r)>>1;
	if(k<=mid)
	{
		Update(ls,k);
	}
	else
	{
		Update(rs,k);
	}
	push_up(p);
}
vector<int>avail;
void FindL(int p,int k1,int k2)
{
	if(Tree[p].l==Tree[p].r)
	{
	//	printf("%d %d&**\n",k2,Tree[p].date2);
		if(Tree[p].date1<=k2)
		{
			avail.push_back(Tree[p].l);
		}
		
		return;
	}
	int mid=(Tree[p].l+Tree[p].r)>>1;
	if(mid>=k1)
	{
		if(Tree[ls].date1<=k2)
		{
			FindL(ls,k1,k2);
		}
		if(Tree[rs].date1<=k2)
		{
			FindL(rs,k1,k2);
		}
	}
	else
	{
		if(Tree[rs].date1<=k2)
		{
			FindL(rs,k1,k2);
		}
	}
}

void FindR(int p,int k1,int k2)
{
	if(Tree[p].l==Tree[p].r)
	{
		//printf("%d %d&**\n",k2,Tree[p].date2);
		if(Tree[p].date2<=k2)
		{
			avail.push_back(Tree[p].l);
		}
		
		return;
	}
	int mid=(Tree[p].l+Tree[p].r)>>1;
	if(mid<k1)
	{
		if(Tree[ls].date2<=k2)
		{
			FindR(ls,k1,k2);
		}
		if(Tree[rs].date2<=k2)
		{
			FindR(rs,k1,k2);
		}
	}
	else
	{
		if(Tree[ls].date2<=k2)
		{
			FindR(ls,k1,k2);
		}
	}
}
signed main()
{
//	freopen("01-01.in","r",stdin);
	memset(dp,0x3f,sizeof(dp));
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld %lld %lld %lld",&a[i].t,&a[i].l,&a[i].r,&a[i].c);
	}
	
	sort(a+1,a+1+m);
	Build(1,1,m);
	for(int i=1;i<=m;i++)
	{
		if(a[i].l==1)
		{
	//		printf("%d %d-\n",i,a[i].c);
			node sf;
			sf.u=i;
			sf.val=a[i].c;
			dp[i]=a[i].c;
			q.push(sf);
			Update(1,i);
		}
	}
	int tot=0;
	while(q.size())
	{
		
		node temp=q.top();
	//	printf("%lld %lld--\n",temp.u,temp.val);
		q.pop();
		if(a[temp.u].r==n)
		{
			printf("%lld",temp.val);
			return 0;
		}
		avail.clear();
		FindL(1,temp.u,a[temp.u].r+a[temp.u].t+1);
		for(int i=0;i<avail.size();i++)
		{
			int vv=avail[i];
			dp[vv]=dp[temp.u]+a[vv].c;
			Update(1,vv);
			node fgf;
			fgf.u=vv;
			fgf.val=dp[vv];
			q.push(fgf);
	//		printf("%d %d\n",vv,dp[vv]);
		}
	//	printf("\n");
		avail.clear();
		FindR(1,temp.u,a[temp.u].r-a[temp.u].t+1);
		for(int i=0;i<avail.size();i++)
		{
			int vv=avail[i];
			dp[vv]=dp[temp.u]+a[vv].c;
			Update(1,vv);
			node fgf;
			fgf.u=vv;
			fgf.val=dp[vv];
			q.push(fgf);
	//		printf("%d %d\n",vv,dp[vv]);
		}
	//	++tot;
	//	printf("%d\n",tot);
	}
	printf("-1");
	
}

标签:date1,date2,int,Tree,mid,DP,简单,dp
来源: https://www.cnblogs.com/kid-magic/p/16428842.html