1.圣诞老人的礼物
问题描述:每箱糖果都有自己的价值和重量,如何选取一定重量的糖果,使得它的总价值最高
解法思路:按照礼物的性价比即价值除以重量来尽可能的选多的性价比的糖果,这是可能的解法,需要通过证实,也有可能选出来的不一定是最优方案.
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const double eps = 1e-6; 比较精度;
struct candy {
int v;int w;
bool operator< (const candy &c) 比较价值除以质量的比值;
( return double (v)/w - double(c.v)/c.w>eps;)
} candies[110];
int main()
{
int n,w;
scanf("%d%d",&n,&w);
for(int i=0;i<n;i++)
scanf("%d%d",&candies[i].v,&candies[i].w); 依次将数据输入保存在结构体candies里;
sort(candies,candies+n);
int totalw=0;
double totalv=0;
for(int i=0;i<n;++i)
{
if(totalw+candies[i].w<=w){ 如果总重量未超过最高质量
totalw+=candies[i].w; 将总价值和总重量都增加
totalv+=candies[i].v;
}
else{
totalv+=candies[i].v*double(w-totalw)/candies[i].w; 如果不能整箱拿就拿到目标质量;
break;
}
}
printf("%.1f",totalv);
return 0;
}
证明: 替换法。对于用非此法选取的最大价值糖果箱序列, 可以将其按价值/重量比从大到小排序后得到: 序列1:a1, a2 …
用序列1和按上述解法选取的序列2依次进行比较: 序列2:b1, b2 …
价值/重量比相同的若干箱糖果,可以合并成一箱,所以两个序列中元素都不重复 对于发现的第一个 ai != bi ,则必有:ai< bi
则在序列1中,用 bi 这种糖果,替代若干重量的 ai 这种糖果,则会使得序列1的总价 值增加,这和序列1是价值最大的取法矛盾
所以:序列1 = 序列2 (序列2不可能是序列1的一个前缀且比序列1短)
2.放雷达
x轴是海岸线,上方是海洋,海中有n(1<n<1000)给定的岛屿坐标(x,y)都是整数,当一个雷达的距离不超过整数d时,可以认为雷达覆盖了该岛屿,雷达只能放在x轴上,问至少要几个雷达才能覆盖全岛屿
解题思路:利用勾股定理可以很快得到岛屿最远里的雷达坐标,所以可以将问题转化为雷达应放在那个区间里面的那个整点好,因此可以开始通过枚举寻找最优解.
重要结论:如果一个雷达覆盖多个区间,按起点顺序排序,最后一个起点就是雷达安放的最佳位置,原因就是雷达的位置最好放在越后面的区间,除非下一个区间和上一个区间没有交集,或者下个区间在上个区间里面的话,就放在里面那个区间里,
代码:
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct Radar{
double start,end;
bool friend operator < (Radar a,Radar b){ 存放坐标的结构体;;
return a.start < b.start;
}
}radar[1005];
int main(){
int n,d,x,y,m,num,flag;
double l,r;
m = 1;
while(scanf("%d%d",&n,&d)){
if(!n && !d)
break;
flag = true;
for(int i = 0;i < n;i++){
scanf("%d%d",&x,&y);
if(y > d)
flag = false;
radar[i].start = x - sqrt(d * d - y * y); 如果结果超过了垂直距离,起点就要前移
radar[i].end = x + sqrt(d * d - y * y); 终点就要后移;
}
if(!flag){
printf("Case %d: -1\n",m++);
continue;
}
sort(radar,radar + n);
num = 1,l = radar[0].start,r = radar[0].end;
for(int i = 1;i < n;i++){
if(radar[i].start >= l && radar[i].end <= r){ 对应情况1
l = radar[i].start;
r = radar[i].end;
}
else if(radar[i].start <= r && radar[i].end >= r) 对应情况2
l = radar[i].start;
else if(radar[i].start > r){ 对应情况3
l = radar[i].start;
r = radar[i].end;
num++;
}
}
printf("Case %d: %d\n",m++,num);
}
return 0;
}
总结:两个问题里都用到了替换法来证明,用随机得到的序列和贪心得到的算法比较后证明贪心得到的算法的正确性,其中反证法是非常使用的,正面证明往往比较难。
俞