• USACO动态规划之背包问题1


    序言

    dp太辣鸡所以要刷题!做完USACO里dp专题的所有题!【然而还有数位dp插头dp的都还不会qwq


    题目+题解

    一、Subset Sums, USACO 1998 Spring


    解题思路

    因为平分,所以以和的一半当总容量做01背包。答案除以2,因为会重复算一次

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 11000
    
    long long f[maxn];
    int main()
    {
    	//freopen("subset.in","r",stdin);
    	//freopen("subset.out","w",stdout);
    	int n,i,j,sum;
    	scanf("%d",&n);
    	sum=(n+1)*n/4;
    	memset(f,0,sizeof(f));
    	f[0]=1;
    	for (i=1;i<=n;i++)
    	 for (j=sum;j>=i;j--)
    	  f[j]=f[j]+f[j-i];
    	if (n*(n+1)%4!=0) printf("0
    ");
    	else printf("%I64d
    ",f[sum]/2);
    	return 0;
    }

    二、[bzoj1739][poj2392]Space Elevator, USACO 2005 Mar


    解题思路

    算是加点限制的多重背包?
    f[i]表示能否搭到高度为i..
    if (f[j-k*a[i].h]) f[j]=1;k枚举使用第i块的个数

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define maxn 410
    
    struct node
    {
    	int h,a,c;
    }a[maxn];int f[maxn*100];
    int mymin(int x,int y){return (x<y)?x:y;}
    bool cmp(node x,node y)
    {
    	if (x.a!=y.a) return x.a<y.a;
    	return (x.h*x.c)<(y.h*y.c);
    }
    int main()
    {
    	//freopen("elevator.in","r",stdin);
    	//freopen("elevator.out","w",stdout);
    	int n,i,j,k;
    	scanf("%d",&n);
    	for (i=1;i<=n;i++)
    	{
    		scanf("%d%d%d",&a[i].h,&a[i].a,&a[i].c);
    		a[i].c=mymin(a[i].c,a[i].a/a[i].h);
    	}
    	memset(f,0,sizeof(f));
    	sort(a+1,a+1+n,cmp);f[0]=1;
    	for (i=1;i<=n;i++)
    	  for (j=a[i].a;j>=0;j--)
    		for (k=a[i].c;k>=0;k--)
    		 if (j>=k*a[i].h && f[j-k*a[i].h]) f[j]=1;
    	// for (i=0;i<=a[n].a;i++)
    	 // if (f[i]) printf("%d
    ",i);
    	for (j=a[n].a;j>=0;j--) if (f[j]) break;
    	printf("%d
    ",j);
    	return 0;
    }


    三、[poj2184]Cow Exhibition,USACO 2003 Fall



    解题思路

    f[i]就表示当智商为i时的最大情商。
    因为有负数,所以有个基准值(设为)xx
    转移:if(f[j-s[i]+xx]!=inf)  f[j+xx]=max(f[j+xx],f[j-s[i]+xx]+q[i]);
    这样的话,因为s[i]的正负性不确定,你并不能知道j-s[i]与j的大小关系(即方程的转移方向)
    所以要对s[i]正负的不同做不同的处理[WA了很久= =还不造为什么。。因为根本没有考虑到orz]

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 1001000
    #define inf -1e9
    
    int f[maxn],s[110],q[110];
    int mymax(int x,int y){return (x>y)?x:y;}
    int main()
    {
    	//freopen("osmrtfun.in","r",stdin);
    	//freopen("osmrtfun.out","w",stdout);
    	int n,i,x,y,j,ln,lm,ans,xx;
    	ln=lm=0;ans=xx=0;
    	scanf("%d",&n);
    	for (i=1;i<=n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		if (x<0 && y<0) continue;
    		s[++ln]=x;q[ln]=y;
    		if (s[ln]>=0) lm+=s[ln];
    		else xx+=-s[ln];
    	}n=ln;int pz=-xx;
    	for (j=0;j<=lm+xx;j++) f[j]=inf;
    	f[xx]=0;
    	for (i=1;i<=n;i++)
    	{
    		if(s[i]>=0)
    		{
    			for(j=lm;j-s[i]>=pz;j--)
    			 if(f[j-s[i]+xx]!=inf)
    			  f[j+xx]=max(f[j+xx],f[j-s[i]+xx]+q[i]);
    		}
    		else
    		{
    			for(j=pz;j-s[i]<=lm;j++)
    			 if(f[j-s[i]+xx]!=inf)
    				f[j+xx]=max(f[j+xx],f[j-s[i]+xx]+q[i]);
    		}
    	}
    	for (i=xx;i<=lm+xx;i++)
    	 if (f[i]>=0) ans=mymax(ans,i-xx+f[i]);
    	printf("%d
    ",ans);
    	return 0;
    }


    四、[bzoj1655]Dollar Dayz,USACO 2006 Jan

    解题思路

    f[i]表示数目为i时的划分的方案数

    f[i]+=f[i-j];水..但是要打高精度!

    [太水了。。代码就不贴了占地]


    五、[bzoj1578]Stock Market,USACO 2009 Feb


    解题思路

    f[i]表示第i天能获得的最多的钱
    f[i]=max(f[i-1],g[f[i-1]]);
    g[i]表示有i钱时得到的最多的钱。
    对每天做01背包,k枚举钱,第j天,第i只股票
    g[k]=max(g[k],g[k-p[i][j-1]]+p[i][j]);
    能一天一天的买卖股票的原因是,今天买了某只股票,若后来在较高点卖出的话,相当于在这期间以没那么高的价格卖了一次又买了一次。若期间的价格都比今天低,那不如在最低的那天再买,弄回前面这种情况。
    最后进行最上面的f[]的转移。

    p.s.我好像有一个点还是TLE的。。。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    const int mx=500000;
    int p[60][110],f[110],g[mx+10];
    int main()
    {
    	//freopen("stock.in","r",stdin);
    	//freopen("stock.out","w",stdout);
    	int n,d,m,i,j,k;
    	scanf("%d%d%d",&n,&d,&m);
    	for (i=1;i<=n;i++)
    	 for (j=1;j<=d;j++)
    	  scanf("%d",&p[i][j]);
    	f[1]=m;
    	for (i=2;i<=d;i++)
    	{
    		for (j=1;j<=f[i-1];j++) g[j]=j;
    		for (j=1;j<=n;j++)
    		 if (p[j][i]>p[j][i-1])
    		 {
    			 for (k=p[j][i-1];k<=f[i-1];k++)
    			  if (g[k]<g[k-p[j][i-1]]+p[j][i]) g[k]=g[k-p[j][i-1]]+p[j][i];
    		 }
    		if (g[f[i-1]]>f[i-1]) f[i]=g[f[i-1]];else f[i]=f[i-1];
    	}
    	printf("%d
    ",f[d]);
    	return 0;
    }


  • 相关阅读:
    Linux 添加环境变量
    postgresql 获取修改列的值
    5月30日周一上午
    周日5月29日
    2016年5月26日
    如何使用Gson(添加到项目里去)
    linux内核分析课程总结()待完善
    5月5日离散课笔记
    4月28日的离散课(还少了一部分)
    2016年4月29日
  • 原文地址:https://www.cnblogs.com/Euryale-Rose/p/6527857.html
Copyright © 2020-2023  润新知