POJ 1018

本文通过两段代码对比,解析了一道ACM编程挑战赛题目中的难点,特别是如何正确处理除法运算中的增长率问题,并给出了正确的实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WA代码,以为是简单动归:

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

#define LEN 101
#define MMAX 0x3f3f3f3f

int main() {
	int n, m, num;
	int i, j;
	int bb, pp, b, p;
	int tmpb, tmpp;
	double tmpmax;
	scanf("%d", &n);
	while(n--) {
		scanf("%d", &m);
		bb = MMAX;
		pp = 0;
		tmpb = 0;
		for(i = 0; i < m; i++) {
			scanf("%d", &num);
			tmpmax = 0;
			for(j = 0; j < num; j++) {
				scanf("%d%d", &b, &p);
				if(1.0*(b)/(pp+p) > tmpmax) {
					tmpmax = 1.0*(b)/(pp+p);
					tmpb = b;
					tmpp = p;
				}
			}
			if(bb > tmpb) {
				bb = tmpb;
			}
			pp += tmpp;
		}
		printf("%.3lf\n", 1.0*bb/pp);
	}
	return 0;
}


 

WA了好几次,后来在Discuss里找到了一组测试数据发现错了

3
1
5 299 325 240 417 383 391 405 512 465 988 
7
6 301 960 482 794 472 196 89 373 514 557 446 1089 
9 126 491 91 1068 123 498 119 251 138 838 386 831 144 349 196 434 355 142 
8 67 926 534 1039 472 123 114 986 382 561 277 922 87 718 413 957 
5 163 378 245 301 67 631 483 563 330 1031 
8 487 1073 137 1055 202 171 296 174 144 911 511 378 322 640 150 261 
2 365 470 105 1029 
1 300 582 
8
4 317 932 464 799 180 803 74 317 
1 226 740 
8 301 186 118 664 414 792 506 564 255 1067 181 675 424 660 125 224 
4 495 858 360 877 74 961 309 679 
9 378 1042 464 1068 249 117 456 719 483 870 361 490 136 616 407 318 493 832 
9 420 308 223 415 368 436 495 294 99 656 176 887 287 168 252 657 317 319 
5 436 605 284 649 445 773 115 704 441 960 
7 71 330 447 647 96 667 433 443 312 532 449 340 122 236 



ans:

0.980
0.132
0.062

 

最后发现这其实就是枚举每一个最小带宽。至于为何不能用动归一次解决这样的问题,因为这里面有除法运算,而除法运算所得结果的增长率是不能单从分子和分母的变化而简单分析的。所以这里就不能简单的用上面的那种方法进行计算。
AC代码(DOWNLOAD了一个):

#include<stdio.h>
#include<string.h>

int main()
{
       int i,j,k,m,min,max,high,low,t,sum;
       int b[101][101],p[101][101],num[101],flag[32767];
       double b_p,mmax;
       scanf("%d",&t);    
       while(t--)                 
       {
              memset(flag,0,sizeof(flag));//初始flag为0,用来标记带宽值
              high=32767;
              low=32767; //正整数的可能最大值
              scanf("%d",&m);                      
              for(i=0;i<m;i++)
              {
                     min=10000;//正整数的可能最大值,取决于测试数据是否变态, int型最大是32767, 一般要尽量开大一点
                     max=1;//正整数的可能最小值
                     scanf("%d",&num[i]);//num[i]为各个设备的备用厂商数目
                     for(j=0;j<num[i];j++)
                     {
                            scanf("%d",&b[i][j]);
                            scanf("%d",&p[i][j]);
                            flag[b[i][j]]=1;//当前贷款带宽标记为1
                            if(max<b[i][j])//当前设备最大值
                                   max=b[i][j];                                 
                            if(min>b[i][j])//当前设备最小值
                                   min=b[i][j];     
                     }
                     if(low>min)//各个设备最小带宽中的最小值
                            low=min;
                     if(high>max)//各个设备最大带宽中的最小值
                            high=max;       
              }                                        
              mmax=0;//最终结果
              for(i=low;i<=high;i++)//结合下面的if(flag[i])则无须次次都是利用循环来获得i值,节省了大量时间
              {
                     if(flag[i])//找出经标记过的带宽值(动态规划说白了就是以空间换取时间)
                     {
                            sum=0;                           
                            for(j=0;j<m;j++)   
                            {                                 
                                   min=32767;    
                                   for(k=0;k<num[j];k++)
                                   {
                                          if(i <=b[j][k])
                                          {
                                                 if(min>p[j][k])//获得当前设备所有供应商中最低的价格
                                                        min=p[j][k];
                                          }
                                   }
                                   sum+=min;                                  
                            }
                            b_p=(double)i/(double)sum; 
                            if(mmax<b_p)
                                   mmax=b_p;
                     }
              }
              printf("%.3lf\n",mmax);
       }
       return 0;

}


 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值