zoukankan      html  css  js  c++  java
  • poj 动态规划专题练习

    http://poj.org/problem?id=2336

    大意是要求一艘船将m个车运到对岸所消耗的最短时间和最小次数

    定义dp[i][j]运送前i个车,当前船上有j个车所消耗的时间,非常容易得到如下ac代码的推导

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=1508;
    const int INF=0x7f7f7f7f;
    int dp[maxn][maxn];
    int w[maxn];
    int main()
    {
    	int i,j,cas,n,m,t;
    	scanf("%d",&cas);
    	while(cas--)
    	{
    		scanf("%d%d%d",&n,&t,&m);
    		for(i=1;i<=m;i++)
    			scanf("%d",&w[i]);
    		for(j=1;j<=n;j++)
    			dp[1][j]=w[1]+t;
    		int sum=w[1]+t,ans=1;
    		for(i=2;i<=m;i++)
    		{
    		//	printf("fuck %d
    ",sum);
    			dp[i][1]=max(sum+t,w[i])+t;
    			for(j=2;j<=n;j++)   
    			{
    				if(w[i]>dp[i-1][j-1]-t)
    					dp[i][j]=dp[i-1][j-1]+w[i]-w[i-1];
    				else
    					dp[i][j]=dp[i-1][j-1];
    			}
    			sum=INF;
    			for(j=1;j<=n;j++)
    				if(sum>dp[i][j])
    					sum=dp[i][j];
    		}
    	/*	for(i=1;i<=m;i++)
    		{
    			for(j=1;j<=n;j++)
    				printf("%dfuck%d ",num[i][j],dp[i][j]);
    			printf("
    ");
    		}*/
    		ans=m/n;
    		if(m%n)	ans++;
    		printf("%d %d
    ",sum,ans);
    	}
    	return 0;
     } 
    

      

      但似乎看起来还是有些不对?这里的状态转移没有类似于dp[i][j]=max(dp[i][j],。。)这样取最优状态的做法,我们这样定义状态可能是多余的。事实上只要dp[i]就可以记录状态了。除了dp以外还有贪心做法:我们运送所有车辆的最短时间,就是要最后一辆车的等待时间最小:第一次运m%n个,然后每次运n个,就可以得到最优解了

    poj 3162 Walking Race

    这题可以概括为两步:

    1.求树上点所能到达的最远点,这类问题可以说和求树上点的直径类似。树上任意点的最远点显然就是直径的两个端点之一

    2.在一个序列中,求满足区间内最大值最小值之差不超过m的最长区间的大小

    问题1是经典的树规,dp[u]为向下走能走到的最远值,dp1[u]为不包括dp[u]的第一个结点所能走到的最大值,tp[u]为向上走所能走到的最大值

    最远点就是max(dp[u],tp[u]),分类讨论可得tp[u]。转移见代码

    问题2也是经典的规划问题,用单调队列可以维护区间最大值和最小值

    我们单调队列的做法是:开两条单调队列,一条维护最大值一条维护最小值。插入元素时,若插入新元素到队尾破坏了队列的单调性,则抛弃原有的队尾元素直到新元素的插入不破坏队列的单调性。每次拿出两个队首的元素进行判断,若max-min>m,删除下标靠前的队首元素,重新判断条件是否成立。若不成立继续删直到成立为止。 若max-min>m,考虑更新答案。

    队列的插入删除手段实际上是根据所求的一种贪心--当更大更靠右的元素在队首,之前入队的元素可以抛弃(嘛,)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int maxn=1000008;
    struct fuck{
    	int u,v,w,next;
    }edge[maxn<<1];
    int tol;
    int head[maxn];
    void init()
    {
    	tol=0;
    	memset(head,-1,sizeof(head));
    } 
    void addedge(int u,int v,int w)
    {
    	edge[tol].u=u;
    	edge[tol].v=v;
    	edge[tol].w=w;
    	edge[tol].next=head[u]++;
    	head[u]=tol++;
    }
    int dp[maxn],dpx[maxn],tp[maxn],dp1[maxn];
    void dfs(int u,int pre)
    {
    	int i;
    	dp[u]=0;
    	for(i=head[u];i!=-1;i=edge[i].next)
    	{
    		int v=edge[i].v;
    		if(edge[i].v==pre)	continue;
    		dfs(v,u);
    		if(dp[v]+edge[i].w>dp[u])
    		{
    			dp[u]=dp[v]+edge[i].w;
    			dpx[u]=v;
    		}
    	}
    }
    void dfs1(int u,int pre)
    {
    	int i;
    	dp1[u]=0;
    	for(i=head[u];i!=-1;i=edge[i].next)
    	{
    		int v=edge[i].v;
    		if(v==pre)	continue; 
    		dfs1(v,u); 
    		if(v!=dpx[u]&&dp[v]+edge[i].w>dp1[u])
    			dp1[u]=dp[v]+edge[i].w;
    	}
    }
    void dfs2(int u,int pre)
    {
    	int i;
    	for(i=head[u];i!=-1;i=edge[i].next)
    	{
    		int v=edge[i].v;
    		if(v==pre)	continue;
    		if(dpx[u]==v)
    			tp[v]=max(dp1[u],tp[u])+edge[i].w;
    		else
    			tp[v]=max(dp[u],tp[u])+edge[i].w;
    		dfs2(v,u);
    	}
    }
    int ans[maxn];
    int mx[maxn];
    int mi[maxn];
    int solve(int n,int m)
    {
    	int mxf=0,mxb=0;
    	int mif=0,mib=0;
    	int res=0;
    	int left=1;
    	for(int i=1;i<=n;i++)
    	{
    		while(mxf>mxb&&ans[mx[mxf-1]]<=ans[i])
    			mxf--;
    		mx[mxf++]=i;
    		while(mif>mib&&ans[mi[mif-1]]>=ans[i])
    			mif--;
    		mi[mif++]=i;
    		if(ans[mx[mxb]]-ans[mi[mib]]<=m) 
    		{
    			int sum=i-left+1;
    			if(sum>res)	res=sum;
    		//	printf("%d %d
    ",left,i);
    		}
    		else
    		{
    		//	printf("%d
    ",i);
    			while(mib<mif&&mxb<mxf&&ans[mx[mxb]]-ans[mi[mib]]>m)
    			{
    				if(mx[mxb]>mi[mib])
    				{
    					left=mi[mib]+1;
    					mib++;
    				}
    				else	
    				{
    					left=mx[mxb]+1;
    					mxb++;
    				}
    			}
    		}
    	}
    	return res;
    }
    int main()
    {
    	int n,m,u,v;
    	while(scanf("%d%d",&n,&m)==2)
    	{
    		init();
    		for(int i=1;i<n;i++)
    		{
    			scanf("%d%d",&u,&v);
    			addedge(u,i+1,v);
    			addedge(i+1,u,v);
    		}
    		dfs(1,-1);
    		dfs1(1,-1);
    		tp[1]=0;
    		dfs2(1,-1);
    		for(int i=1;i<=n;i++)
    			ans[i]=max(dp[i],tp[i]);
    	//	for(int i=1;i<=n;i++)	printf("%d ",ans[i]);
    		int res=solve(n,m);
    		printf("%d
    ",res);
    	}
    	return 0;
     } 
    

      

    poj1947 Rebuilding Roads

    经典树规,求在一颗树中获得一颗节点数为p的子树最少需要砍掉多少条边

    这题有两个没有说明的地方,其一是,数据中给出的子树总是以1为根的。其二是,所求的子树可以不包含根节点

    定义dp[u][i][j]为以u为根节点的树处理了前i个子节点并获得一颗j节点的子树最少需要砍掉的边,初始dp[u][0][1]为u节点的度数,然后有以下转移:

    
    
    if(dp[u][idx-1][j-x]!=INF&&dp[v][du[v]][x]!=INF)
    
    {
    	dp[u][idx][j]=min(dp[u][idx-1][j-x]+dp[v][du[v]][x]-1,dp[u][idx][j]);
    }
    
    
    
    之后根据枚举根节点可以这样求得答案:
    int ans=dp[1][du[1]][p];
    for(int i=2;i<=n;i++)
    	if(dp[i][du[i]][p]+1<ans)
    		ans=dp[i][du[i]][p]+1;
    

      

    dp的过程也不难理解,就是相当于将每颗子树看成一个分组,将每颗子树可能取的值当作分组中的物品然后跑一遍分组背包。理解分组背包的情况下不难想到
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=158;
    const int INF=0x7f7f7f7f;
    struct fuck{
    	int u,v,next;
    }edge[maxn<<1];
    int tol;
    int head[maxn];
    void init()
    {
    	tol=0;
    	memset(head,-1,sizeof(head)); 
    }
    void addedge(int u,int v)
    {
    	edge[tol].u=u;
    	edge[tol].v=v;
    	edge[tol].next=head[u];
    	head[u]=tol++;
    }
    int dp[maxn][maxn][maxn];
    int du[maxn];
    int num[maxn];
    void dfs(int u,int p)
    {
    	int i;
    	dp[u][0][1]=du[u];
    	num[u]=1;
    	int idx=0;
    	for(i=head[u];i!=-1;i=edge[i].next)
    	{
    		int v=edge[i].v;
    		dfs(v,p);
    		num[u]+=num[v];
    		idx++;
    		for(int x=1;x<=p;x++)
    			dp[u][idx][x]=dp[u][idx-1][x];
    		for(int j=1;j<=p;j++)
    			for(int x=1;x<j;x++)
    			{
    				if(dp[u][idx-1][j-x]!=INF&&dp[v][du[v]][x]!=INF)
    				{
    					dp[u][idx][j]=min(dp[u][idx-1][j-x]+dp[v][du[v]][x]-1,
    						dp[u][idx][j]);
    				}
    			}
    	}
    }
    int main()
    {
    	int n,p,u,v;
    	while(scanf("%d%d",&n,&p)==2)
    	{
    		memset(dp,INF,sizeof(dp));
    		init();
    		memset(du,0,sizeof(du));
    		for(int i=1;i<n;i++)
    		{
    			scanf("%d%d",&u,&v);
    			addedge(u,v);
    			du[u]++; 
    		}
    		dfs(1,p);
    		int ans=dp[1][du[1]][p];
    		for(int i=2;i<=n;i++)
    			if(dp[i][du[i]][p]+1<ans)
    				ans=dp[i][du[i]][p]+1;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    (翻译)Entity Framework技巧系列之九 Tip 35 36
    (翻译)Entity Framework技巧系列之十 Tip 37 41
    (翻译)Entity Framework技巧系列之一 Tip 1 5
    (翻译)Entity Framework技巧系列之五 Tip 16 – 19
    [翻译]C#数据结构与算法 – 第五章栈与队列(Part 1)
    [翻译]C#数据结构与算法 – 第六章BitArray类
    (翻译)Entity Framework技巧系列之七 Tip 26 – 28
    (翻译)Entity Framework技巧系列之四 Tip 13 – 15
    (翻译)Entity Framework技巧系列之二 Tip 6 8
    使用YCSB测试mongodb
  • 原文地址:https://www.cnblogs.com/bitch1319453/p/6765402.html
Copyright © 2011-2022 走看看