pat_1002 jdoj _1462 hduoj _1864

        这题开始读的时候还有些读不懂主要是这个单词"polynomials”是多项式的意思,这题理解题目意思就简单了,但是还有一点要注意了,就是为虽然A和B中都有同一个整数代表的值,但是A和B相加后结果会为0这种情况不应该出现在结果中,题目链接http://pat.zju.edu.cn/contests/pat-a-practise/1002,再看了别人写的代码后又比自己写的简单现付别人写的代码如下(经过本人重新编写过):

#include<iostream>
using namespace std;
double a[1010];
int main()
{
	int k;
	int i,j;
	for(i = 0;i < 2;i++)
	{
		cin>>k;
		for(j = 0;j < k;j++)
		{
			int index;
			double value;
			cin>>index>>value;
			a[index] += value;
		}
	}
	int sum = 0;
	for(i = 1000;i >= 0;i--)
	{
		if(a[i] != 0.0)sum++;
	}
	cout<<sum;
	for(i = 1000;i >= 0;i--)
	{
		if(a[i] != 0.0)printf(" %d %.1lf",i,a[i]);
	}
	cout<<endl;
	return 0;
}
此处的输出形式值得学习,在输出一系列数时我已经习惯用前导输出,但是用后导输出很方便而且一些特殊情况也可以满足,但是此题最大的学习的地方就是用一个数组直接存储所需要的数,而不必进行一系列的麻烦的比较,此处开的数组大小把所有情况都包进去了......在其他很多很多地方都用到这种思想,最近做的一些题比如0-1背包问题如九度oj上1462题( http://ac.jobdu.com/problem.php?pid=1462)实际上就是一个0-1背包问题,现在来总结一下0-1背包问题

      先从装载问题谈起,问题描述有n件物品有一定的的重量wi(1<=i<=n),要把这些物品装进载重为w的船上,要求船上装着的物品的总重量最大但又要不超过船的载重W,这样的题目看到之后该如何下手?该怎样去思考?不管怎样可以肯定的一点就是此问题必须考虑所有物品的重量的组合,在这些组合中比W的可以弃去,现在的问题是如何统计这些组合,思考......其实最后只要统计这些组合中物品的重量不超过W的总重量有哪些,此时可以开一个大小为W+1的bool型数组,来记录是否有重量为i(1=<i<=W)的组合,初始化时为false,但是接下来的问题是如何才能找到这些组合的总重量的值?在这里就有一种非常巧妙的方法了,就是用前面的bool型记录有哪些重量,i从1到n开始用重量wi去遍历数组其中i之前的数组已经遍历过了,找到了之前的所有组合,现在增加i的时候又增加了新的一些组合,现在的问题是如何去遍历?是从前往后还是从后往前?现在想一下从前往后遍历的情况,此种情况是不可以的,因为每一个wi可以重复计算多次,故用从后往前可以消除这种弊端......这样就所有情况都讨论完毕了,可以得出最大的装载重量。还有一种是网上看到的情况,用动态规划来求解(dp),其实说白了就是我上面写的思路。此时也是用到了前面所说的用一个数组来保存所有状态。 还有一道类似的题hduoj1864,(http://acm.hdu.edu.cn/showproblem.php?pid=1864)此题也是0-1背包问题,但是需要一点点的变化,就是把double类型的数变为整型的数后用0-1背包来求解,但是还有许多小的地方需要注意,比如在输入一个整型和一个字符之间时要用到要用到getchar()函数来消除前导空格

代码如下

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
bool s[3000010];
int main()
{
	int N,i,j;
	double Q;
	vector<int> vec;
	while(cin>>Q>>N && N != 0)
	{
		while(!vec.empty())vec.pop_back();
		for(j = 0;j < N;j++)
		{
			int m;
			char a;
			double p;
			double sum = 0;
			bool flag = true;
			cin>>m;
			double sumA = 0,sumB = 0,sumC = 0;
			for(i = 0;i < m;i++)
			{
				getchar();     //用来消除前导空格......有点费解
				scanf("%c:%lf",&a,&p);
				//cout<<a<<" "<<p<<endl;
				if(flag)
				{
					if(sumA <= 600.00 && sumB <= 600.00 && sumC <= 600.00)
					{
						if(a != 'A'  && a != 'B' && a != 'C')
						{
							flag = false;
						}
						else
						{
							if(a == 'A')sumA += p;
							if(a == 'B')sumB += p;
							if(a == 'C')sumC += p;
						}
					}
					else flag = false;
				}
			}
			sum = sumA + sumB + sumC;
			if(sum > 1000.00)flag = false;
			if(flag)
			{
				vec.push_back((int)(sum*100));
			}
		}
		memset(s,0,sizeof(s));
		int temp = Q*100;
		for(i = 0;i < vec.size();i++)
		{
			for(j = temp - vec.at(i);j > 0;j--)
			{
				if(s[j])
				{
					s[j + vec.at(i)] = true;
				}
			}
			s[vec.at(i)] = true;
		}
		for(i = temp;i >0;i--)
		{
			if(s[i])break;
		}
		//cout<<i<<endl;
		Q = i*1.0/100.00;
		printf("%.2lf\n",Q);

	}
	return 0;
}
不过此题似乎听说还可以用深度优先搜索(dfs),其实仔细想想的确可以用深度优先搜索,就是不断的进行深度优先搜索,最终找到满足题目所要求的条件,不过可惜了超时了现附代码如下:

#include<iostream>
#include<cstring>
using namespace std;
double a[35];
bool f[35];
int cou;
double max1;
double num;
double Q;
void dfs(int index)
{
	num += a[index];
	//cout<<index<<" "<<num<<endl;
	if(num <= Q && num > max1)max1 = num;
	if(num > Q)return;
	f[index] = true;
	int i;
	for(i = 0;i < cou;i++)
	{
		if(f[i] == false && num <= Q)dfs(i);
	}
	f[index] = false;
	num -= a[index];
}
int main()
{
	int N,i,j;
	
	double type[3];
	while(scanf("%lf %d",&Q,&N) == 2 && N != 0)
	{
		int items;
		cou = 0;
		for(i = 0;i < N;i++)
		{
			type[0] = 0.0,type[1] = 0.0,type[2] = 0.0;
			scanf("%d",&items);
			char t;
			double p;
			bool flag = true;
			double sum = 0;
			for(j = 0;j < items;j++)
			{
				getchar();
				scanf("%c:%lf",&t,&p);
				//cout<<t<<endl;
				if(t != 'A' && t != 'B' && t != 'C')flag = false;
				else
				{
					type[t - 'A'] += p;
				}
			}
			sum = type[0] + type[1] + type[2];
			
			if(type[0] > 600.0 || type[1] > 600.0 || type[2] > 600.0 || sum > 1000.0)flag = false;
			//cout<<sum<<endl;
			if(flag)
			{
				a[cou] = sum;
				cou++;
			}
		}
		//for(i = 0;i < cou;i++)cout<<a[i]<<" ";
		//memset(f,0,sizeof(f));
		max1 = 0.0;
		for(i = 0;i < cou;i++)
		{
			num = 0.0;
			memset(f,0,sizeof(f));
			dfs(i);
		}
		printf("%.2lf\n",max1);
	}
	return 0;
}
此时的深度优先搜索把所有的情况都考虑进去了故时间复杂度为O(n!),没有考虑剪纸的情况了,故超时,但是此段代码还是有许多值得学习的地方,比如说如何来保存深搜路径上的所有所经过的节点的值,由于这个值只需要和不超过给定条件的最大值进行比较,故不需要真正的保存所有的值,只需要保存一个当前值就可以了,那么怎么保存当前值呢?设置一个全局变量,在深搜开始时加上这个节点的值,深搜完这个节点时减去这个值,当然还要设置标记每个节点是否访问过的值,和前面全局变量设置方法一样, 有一个问题是有没有更为方便的保存经过路径上的值,当然有,就是把值通过变量传递给深度优先搜索函数 ,进行修改后的代码如下:

#include<iostream>
#include<cstring>
using namespace std;
double a[35];
bool f[35];
int cou;
double max1;
double Q;
void dfs(double sum,int index)
{
	if(max1 == Q)return;
	if(sum > max1 && sum <= Q)max1 = sum;
	int i;
	for(i = index;i < cou;i++)
	{
		if(sum + a[i] <= Q)dfs(sum + a[i],i + 1);
		else break;
	}
}
int main()
{
	int N,i,j;
	
	double type[3];
	while(scanf("%lf %d",&Q,&N) == 2 && N != 0)
	{
		int items;
		cou = 0;
		for(i = 0;i < N;i++)
		{
			type[0] = 0.0,type[1] = 0.0,type[2] = 0.0;
			scanf("%d",&items);
			char t;
			double p;
			bool flag = true;
			double sum = 0;
			for(j = 0;j < items;j++)
			{
				getchar();
				scanf("%c:%lf",&t,&p);
				if(t != 'A' && t != 'B' && t != 'C')flag = false;
				else
				{
					type[t - 'A'] += p;
				}
			}
			sum = type[0] + type[1] + type[2];
			
			if(type[0] > 600.0 || type[1] > 600.0 || type[2] > 600.0 || sum > 1000.0)flag = false;
			if(flag)
			{
				a[cou] = sum;
				cou++;
			}
		}
		max1 = 0.0;
		dfs(0.0,0);
		printf("%.2lf\n",max1);
	}
	return 0;
}
但是这段代码却是有点令人费解仔细多想几遍应该就会好些吧,这里还引起了自己的一些注意就是在深度优先搜索的时候在向下传递路径变量时应该怎样传递,最好用参数进行传递,说道深度优先搜索,会想起前段时间做的一道深度优先搜索的题目,这里就不在深入的进行说了。

    扯了这么久,还是回到原来的主题上来吧,但是还是先接着把0-1背包问题说完,具体题目参考(http://acm.hdu.edu.cn/showproblem.php?pid=2602),这是一个典型的0-1背包问题此题开始看到的时候还以为是载重问题的变体,后来发现自己想错了,想错的原因是因为把载重问题的bool型值变成了int变量值保存其价值,这样就会每次遍历都会存储载重为i的价值,如果此价值大于当前数组中存储的价值那么就更新此价值,但是此时出现问题了,当更新此价值时,前面遍历过要用到此价值的数组中的值不能更新,怎么办呢?还没想出来,不知道.....此种方法暂时不行。现在只有换一种方法了,在网上搜了一下关于讲解01背包的问题的博客此时可以用dp来做,对于第i件物品有两种选着,要么放进去要么不放进去,什么时候放进去什么时候不放进去要具体分析,设dp[i][j]表示前i件物品放进载重为j的船中的最大价值,然后dp[i][j] = max{dp[i - 1][j],dp[i - 1][j - w[i] + value[i]},(为什么是这样,为什么,为什么,其实其中的细节真的没想清楚啊,弱爆了!不管啦,先写了再说吧)最后得到的就为最大值了,现付代码如下:

#include<iostream>
#include<cstdio>
using namespace std;

int dp[1010][1010];
int main()
{
	int t;
	int N,V;
	int i,j;
	int value[1010];
	int volume[1010];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d %d",&N,&V);
		for(i = 1;i <= N;i++)scanf("%d",&value[i]);
		for(i = 1;i <= N;i++)scanf("%d",&volume[i]);
		for(i = 0;i <= V;i++)dp[0][i] = 0;
		for(i = 0;i <= N;i++)dp[i][0] = 0;
		for(j = 1;j <= N;j++)
		{
			for(i = 0;i <= V;i++)
			{
				dp[j][i] = dp[j - 1][i];
				if(i >= volume[j] && dp[j][i] < dp[j - 1][i - volume[j]] + value[j])dp[j][i] = dp[j - 1][i - volume[j]] + value[j];
			}
		}
		printf("%d\n",dp[N][V]);
	}
	return 0;
}

  据说还有空间复杂度为O(N)的情况,现付代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int val[1010];
int vol[1010];
int res[1010];
int main()
{
	int N,V;
	int T,i,j;
	cin>>T;
	while(T--)
	{
		cin>>N>>V;
		for(i = 1;i <= N;i++)cin>>val[i];
		for(i = 1;i <= N;i++)cin>>vol[i];
		memset(res,0,sizeof(res));
		for(i = 1;i <= N;i++)
		{
			for(j = V - vol[i];j > 0;j--)
			{
				if(res[j] != 0 && res[j + vol[i]] < res[j] + val[i])res[j + vol[i]] = res[j] + val[i];
			}
			if(res[vol[i]] < val[i])res[vol[i]] = val[i];
		}
		int max_value = 0;
		for(i = 1;i <=  V;i++)if(res[i] > max_value)max_value = res[i];
		cout<<max_value<<endl;

	}
	return 0;
}






 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值