problem 1558

来源:互联网 发布:淘宝推广用什么软件好 编辑:程序博客网 时间:2024/06/03 18:20

Euro Efficiency

题目讲了一大通,其实就是在说找硬币。转化成数学语言就是:

      先给定6个数a[0…6],对于所有满足n = 的组合,求出使用a[0…6]的个数最少的组合的a[0…6]的个数(记为r[n]),其中1<=n<=1001<=a[0…6]<=100

输出max{ r[n] | 1 <= n <= 100 } /100。

考虑DP,容易看出r[n]=min{ r[|n+/-a[0...6]|] }+1。碰到了一个问题,当去求r[n]的时候需要同时用到比n大(n+a[0...6])的和比n小(|n-a[0...6]|有可能比n小)的,也就是说按传统DP从1到100递增的顺序填r[n]是不可行的。

那么就想到用递归,结果搞了半天觉得太麻烦了,理不请楚。

     其实问题很简单的。反过来想:初始时候我们已知了r[a[0...6]]=1,那么r[a[0…6]+/-a[0…6]]=2(去掉不满足1<=n<=100的情况),就是从已知的推出未知的。

不断重复如下:

n0100

f = |a[0…6] +/- n|

if(f > 0 && f <= 100)

r[f] = r[f] > (r[k] + 1) ? r[k] + 1 : r[f]

那么最多要重复多少次呢?显然最多只需做max{ r[n] | 1 <= n <= 100 } 次,不过我们还没有求得,这里采用边算边求的方法。

1558 C 00:00.00 392K

 

#include<stdio.h>
#include<string
.h>
#include
<stdlib.h>
#include
<math.h>
int a[6];
char r[101
];
void
 solve()
{
    
int i,j,k,sum = 1,max = 0,cnt = 0
;
    memset(r,
100,sizeof
(r));
    scanf(
"%d %d %d %d %d %d",&a[0],&a[1],&a[2],&a[3],&a[4],&a[5
]);
    r[a[
0]] = r[a[1]] = r[a[2]] = r[a[3]] = r[a[4]] = r[a[5]] = 1
;
    
while(1
)
    {
        
for(j = 0; j < 6; j++
)
        {
            max 
= 0
;
            
for(k = 1; k <= 100 - a[j]; k++
)
            {
                
int f = a[j] +
 k;
                
if(f > 0 && f <= 100
)
                {
                    r[f] 
= r[f] > (r[k] + 1? r[k] + 1
 : r[f];
                    max 
= max > r[f] ?
 max : r[f];
                }
                f 
= abs(a[j] -
 k);
                
if(f > 0 && f <= 100
)
                {
                    r[f] 
= r[f] > (r[k] + 1? r[k] + 1
 : r[f];
                    max 
= max > r[f] ?
 max : r[f];
                }
            }
        }
        
if(max <= ++
cnt)
            
break
;
    }
    
for(i = 2; i <= 100; i++
)
        sum 
+=
 r[i];
    printf(
"%.2f %d ",sum / 100.0
,max);
}
void
 main()
{
    
int
 t;
#ifndef ONLINE_JUDGE
    freopen(
"test.txt","r",stdin);
#endif 
    
while(scanf("%d",&t)!=EOF ) 
        
while(t--
)
            solve();
#ifndef ONLINE_JUDGE
    fclose(stdin);
#endif
}