HDU1203 I NEED A OFFER!0、1背包及空间优化

I NEED A OFFER!

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5280    Accepted Submission(s): 1799


Problem Description
Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了。要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的。Speakless没有多少钱,总共只攒了n万美元。他将在m个学校中选择若干的(当然要在他的经济承受范围内)。每个学校都有不同的申请费用a(万美元),并且Speakless估计了他得到这个学校offer的可能性b。不同学校之间是否得到offer不会互相影响。“I NEED A OFFER”,他大叫一声。帮帮这个可怜的人吧,帮助他计算一下,他可以收到至少一份offer的最大概率。(如果Speakless选择了多个学校,得到任意一个学校的offer都可以)。
 

Input
输入有若干组数据,每组数据的第一行有两个正整数n,m(0<=n<=10000,0<=m<=1000)
后面的m行,每行都有两个数据ai(整型),bi(实型)分别表示第i个学校的申请费用和可能拿到offer的概率。
输入的最后有两个0。
 

Output
每组数据都对应一个输出,表示Speakless可能得到至少一份offer的最大概率。用百分数表示,精确到小数点后一位。
 

Sample Input
10 3 4 0.1 4 0.2 5 0.3 0 0
 

Sample Output
44.0%
Hint
You should use printf("%%") to print a '%'.
 
  刚刚学习到背包这个章节,这道题目可以说是在经典背包问题上的一点改进,题中首先将所求值变为了概率 “至少一份offer的最大概率” 现假设被A学校录取概率为 a,被B学校录取概率为 b,则所求概率为1-(1-a)*(1-b), 这个应该好理解吧,知道了这点应该好办了吧,我们把判断条件有经典的相加变为 一个函数的判断:
  float probability(float a,float b)
  {
      return 1-(1-a)*(1-b);
  }
  float t=probability(p[i],c[j-w[i]]); 
           if(t>c[j])
                c[j]=t;
有了思路写出代码来:
  
View Code
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<math.h>
4 #include<string.h>
5 #include<time.h>
6
7  float c[1001][10001];
8  int w[1001];
9  float p[1001];
10
11  float probability(float a,float b)
12 {
13 return 1-(1-a)*(1-b);
14 }
15
16 int main()
17 {
18 int N,M;
19 while(scanf("%d%d",&N,&M),M|N)
20 {
21 for(int i=0;i<=1000;++i)
22 for(int j=0;j<=10000;++j)
23 c[i][j]=0;
24 for(int i=1;i<=M;++i)
25 scanf("%d %f",&w[i],&p[i]);
26 for(int i=1;i<=M;++i)
27 for(int j=0;j<=N;++j)
28 {
29 if(w[i]<=j)
30 {
31 float t=probability(p[i],c[i-1][j-w[i]]);
32 if(t-c[i-1][j]>1e-6)
33 c[i][j]=t;
34 else
35 c[i][j]=c[i-1][j];
36 }
37 else
38 c[i][j]=c[i-1][j];
39 }
40 printf("%.1f%%\n",c[M][N]*100);
41 }
42 return 0;
43 }
 
  这样做的结果是超时......
 当然我把10001改成8000 也过了......   看来有时候题中条件是纸老虎啊!!!
    除了“耍赖”还有其他方法吗,答案是肯定的,我们注意到在每次的更新中,看起来好像是依托于上一次的固定值,其实不然,首先每次更新是覆盖了上一次的所有值,也就是上一次的结果已经无须保留,其次此类问题不像数塔般由于在时间或地域上有前后的依赖性,存在一个类似分叉抉择,在数据模拟上(由于有了两个坐标)必须开辟二维数组,但是使用一维数组还有一个地方要改动,就是每次更新从后面开始,即从负荷(在不同的体型中可能是不同的意思)限制大的一端开始,这样就巧妙的利用了用一维数组使用到了二维数组中的“上一次”操作的值了。代码如下:
View Code
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<math.h>
4 #include<string.h>
5 #include<time.h>
6
7 float c[10005];
8 int w[1001];
9 float p[1001];
10
11 float probability(float a,float b)
12 {
13 return 1-(1-a)*(1-b);
14 }
15
16 int main()
17 {
18 int N,M;
19 while(scanf("%d%d",&N,&M),M|N)
20 {
21 for(int i=0;i<=10000;++i)
22 c[i]=0;
23 for(int i=1;i<=M;++i)
24 scanf("%d %f",&w[i],&p[i]);
25 for(int i=1;i<=M;++i)
26 for(int j=N;j>=0;--j)
27 {
28 if(w[i]<=j)
29 {
30 float t=probability(p[i],c[j-w[i]]);
31 if(t>c[j])
32 c[j]=t;
33 }
34 }
35 printf("%.1f%%\n",c[N]*100);
36 }
37 return 0;
38 }
原文地址:https://www.cnblogs.com/Lyush/p/2031974.html