pat1068

给你n个数和一个给点的m,问你这n个数中能否有某些数的和等于m,如果有,输出字典序最小的那个解。

本题算是一个背包吧,假如不要求输出解,完全可以一维的状态,只要注意下后推和前推,一开始确实被这个坑了,用一维的话,输出的方案会有重复使用某个数的情况,后来改成二维,二维就不存在前推后推的区别了,学背包问题,如果不懂一维二维前推后推的区别,那只能说连皮毛也没学到。

dp[i][j]表示前i个数中能否组成j,能则赋值1,不能则0。

注意这里是前i个数,不是刚好,比如说前5个数,但是并不是这5个数都用了,可能用了第1,3,4个这3个数,这也就是动态规划的精髓了。因为我把已经求解的存在dp数组里了

假如求解dp[i][j]时,假如去掉第i个数的状态(dp[i-1][j-a[i]])已经可达了,那么dp[i][j]同样可达,假如dp[i-1][j-a[i]]不可达,那么很简单,第i个数取了也没用,那么就不取,那么dp[i][j]=dp[i-1][j]。

为了求字典序最小的解,需要一开始按照a[i]降序排序,用pre[i][j]记录这个状态由哪个数转过来的,最后输出即可。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
int n,m;
int dp[10001][101];
int pre[10001][101];
int a[10001];
int ans[10001];
bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int i,j;
		memset(dp,0,sizeof(dp));
		memset(pre,-1,sizeof(pre));
		int sum=0;
		for(i=1;i<=n;i++)
			scanf("%d",a+i);

		sort(a+1,a+n+1,cmp);
		for(i=0;i<=n;i++)
			dp[i][0]=1;
		for(i=1;i<=n;i++)
			for(j=a[i];j<=m;j++)
			{
				if(dp[i-1][j-a[i]]==1)
				{
					dp[i][j]=1;
					pre[i][j]=i;
				}
				else
					dp[i][j]=dp[i-1][j];
			}
			
		if(dp[n][m]==0)
			printf("No Solution\n");
		else 
		{
			int cnt=0;
			while(m!=0)
			{
				if(pre[n][m]!=-1)
				{
					int tmp=pre[n][m];
					ans[++cnt]=a[tmp];
					n=tmp-1;
					m-=a[tmp];
				}
				else 
					n--;
			}
			for(i=1;i<cnt;i++)
				printf("%d ",ans[i]);
			printf("%d\n",ans[cnt]);
		}

	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值