第四周学习体会

这个周还是学习了贪心算法,首先做贪心算法的题之前首先要根据题意寻找贪心策略,这样才能获得盖提的最优解。之前做题有时候我在没找贪心策略前就着手做题,虽然侥幸做出了几道较简单的题,但是越到后面用的时间就越长,有时候甚至需要两天的时间去解决一道题,这让我意识到了做提前找贪心策略的重要性。还有就是这次贪心训练是我第一次接触英语的编程题,对于我来说理解清楚题意成了一个问题,比如在做Integer Intervals这道题的时候,由于我对题意的理解有误,让我白费了功夫,给我了一个很大的教训,以后做英语编程题之前我一定会清楚的读懂题目的含义在着手做题。最后一点是在我做题的过程中虽然我了解了STL的知识,但做题的时候还是习惯用最早的方法解决问题,而不是尝试用STL的知识解决,在之后做题的过程中,我一定要加强对STL知识的使用,让他通汇贯通。

下面这道题用了好长时间才ac的一道题
Communication System
描述
We have received an order from Pizoor Communications Inc. for a special communication system. The system consists of several devices. For each device, we are free to choose from several manufacturers. Same devices from two manufacturers differ in their maximum bandwidths and prices.
By overall bandwidth (B) we mean the minimum of the bandwidths of the chosen devices in the communication system and the total price § is the sum of the prices of all chosen devices. Our goal is to choose a manufacturer for each device to maximize B/P.
输入
The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by the input data for each test case. Each test case starts with a line containing a single integer n (1 ≤ n ≤ 100), the number of devices in the communication system, followed by n lines in the following format: the i-th line (1 ≤ i ≤ n) starts with mi (1 ≤ mi ≤ 100), the number of manufacturers for the i-th device, followed by mi pairs of positive integers in the same line, each indicating the bandwidth and the price of the device respectively, corresponding to a manufacturer.
输出
Your program should produce a single line for each test case containing a single number which is the maximum possible B/P for the test case. Round the numbers in the output to 3 digits after decimal point.
样例输入
1 3
3 100 25 150 35 80 25
2 120 80 155 40
2 100 100 120 110
样例输出
0.649

#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
using namespace std;
int main()
{
    int t,n;
    pair<int , int>a[1000];
    int b[1000],p[1000];
    int c,d;
    cin >>t;
    while(t--)
    {
        int num=0;
        int k = 0x3fffff;;
        memset(b,0,sizeof(b));
        cin >>n;
        while(n--)
        {
            memset(a,0,sizeof(a));
            int m;
            cin >>m;
            for (int i=0;i<m;i++)
            {
                cin >>a[i].first>>a[i].second;
                if(i==0)
                {
                    c=a[i].first;
                    d=a[i].second;
                }
            }
            for (int i=1;i<m;i++)
            {
                if(a[i].first>c)
                {
                    c=a[i].first;
                    d=a[i].second;
                }
                else if(a[i].first==c)
                {
                    if(a[i].second<d)
                    {
                        d=a[i].second;
                    }
                }
            }
            b[num]=c;
            p[num]=d;
            num++;
        }
            int sum=0;
            for (int i=0;i<num;i++)
            {
                if(k<b[i])
                    k=b[i];
                sum+=p[i];
            }
            cout <<fixed<<setprecision(3)<<(double)k/sum<<endl;
    }
}

这个是我之前出错的代码,这道题告诉你了通信设备的带宽和价格,让你在这个前提下求出最大的B/P,我一开始对这道题贪心策略的理解是只需要我找出三组实验数据中最低的价格P然后就能知道其对应的带宽B,然后再三组带宽中选取最大的B就能求出最大的B/P,但是输出错误。我又将程序改成首先选取最大的B,然后再对应的P中选取最小的,结果还是错误。
这两种贪心策略我犯了相同的错误就是只顾及两组数据其一没有统筹计算,这道题的贪心策略应该是首先找到所有输入的带宽中的最大值和最小值,然后通过for循环先标记最小的B和与之对应的P,然后如果接下来的数据同时满足带宽大于之前存储的数据并且价格小于之前存储的数据,然后把每一个满足条件的带宽除以价格之和的最小值就可以求出最大的B/P。

ac的代码如下:

#include<iostream>
#include<algorithm>
#include<iomanip>
using namespace std;
int a[10001][10001],b[10001][10001];
int num[10001];
int main()
{
	int t,i,j;
	cin>>t;
		while(t--)
		{
			int n;
			cin>>n;
			int max=0,min=999999;
            for(i=0;i<n;i++)
			{
				cin>>num[i];
				for(j=0;j<num[i];j++)
				{
					cin>>a[i][j]>>b[i][j];
					if(a[i][j]>max)
						max=a[i][j];
					if(a[i][j]<min)
						min=a[i][j];
				}
			}
			double ans=0.0;
			for(int m=min;m<=max;m++)
			{
				int sum=0;
				for(i=0;i<n;i++)
				{
				  int minCost=999999;
				  for(j=0;j<num[i];j++)
				  {
				    if(a[i][j]>=m&&b[i][j]<minCost)
						minCost=b[i][j];
				  }
				   sum+=minCost;
				}

				if(1.0*m/sum>ans)
				{
				    ans=1.0*m/sum;
				   
				}
			}

			cout<<fixed<<setprecision(3)<<ans<<endl;

		}
	return 0;
}

Integer Intervals
描述
An integer interval [a,b], a < b, is a set of all consecutive integers beginning with a and ending with b.
Write a program that: finds the minimal number of elements in a set containing at least two different integers from each interval.
输入
The first line of the input contains the number of intervals n, 1 <= n <= 10000. Each of the following n lines contains two integers a, b separated by a single space, 0 <= a < b <= 10000. They are the beginning and the end of an interval.
输出
Output the minimal number of elements in a set containing at least two different integers from each interval.
样例输入
4
3 6
2 4
0 2
4 7
样例输出
4

这是我一开始出错的程序

#include<iostream>
#include<algorithm>
using namespace std;
struct contact
{
    int a;
    int b;
};
bool cmp(contact m,contact n)
{
    return m.b<n.b;
}
int main()
{
    contact m[10001];
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    cin>>m[i].a>>m[i].b;
    sort(m,m+n,cmp);
    int sum=0;
    for(int i=0;i<n-1;i++)
    {
        int j=i+1;
        if(i==0)
        {
            if(m[i].b<m[j].a) sum+=3;
            else if(m[i].b==m[j].a) sum+=2;
            else if(m[i].b>m[j].a) sum+=1;
        }
        else
        {
            if(m[i].b<m[j].a) sum+=2;
            else if(m[i].b==m[j].a) sum+=2;
            else if(m[i].b>m[j].a) sum+=1;
        }
    }
    cout<<sum<<endl;
    return 0;
}

这道题的题意用示意图大致可以如下表示:
0 1 2 3 4 5 6 7
----- (0 to 2)
----- (2 to 4)
------- (3 to 6)
------- (4 to 7)
要找的就是一个集合,集合与数据的每个集合的交集至少有2个元素,求集合最少的数的个数。
我一开始找的贪心策略是先把数据进行升序排序,然后第一个在数组的第一个元素分成两种情况。如果是第一个区间且前一个区间的尾部元素小于后一个区间的首部元素,sum就加3,如果不是以一个元素,且前一个区间的尾部元素小于后一个区间的首部元素,sum就加2。然后等于和大于的情况分别加2和加1,但是我没有注意到多次首部元素同时相同的情况,所以这个贪心策略是错误的。
最后找到的贪心策略是先按照结尾数排序,第一个集合找末尾2个数,第二个集合,现根据前面判断已经有几个数在交集中了,如果够2个就跳到第三个集合,如果不够就从末尾找,往前补够2个,第三及以后的类推。

ac的代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int a,b;
}p[10005];
int n,ans;
bool cmp(node x,node y)
{
    return x.b<y.b;
}
bool v[10005];
int main()
{
    while(scanf("%d",&n)!=EOF){
    for(int i=0;i<n;i++)scanf("%d%d",&p[i].a,&p[i].b);
    sort(p,p+n,cmp);
    memset(v,0,sizeof(v));
    v[p[0].b-1]=1;
    v[p[0].b]=1;
    ans=2;
    for(int i=1;i<n;i++)
    {
        int num=2;
        for(int j=p[i].a;j<=p[i].b;j++)
        {
            if(v[j]==1)num--;
        }
        if(num>0)
        while(num)
        {
            v[p[i].b-num+1]=1;
            ans++;
            num--;
        }
    }
    cout<<ans<<endl;}
    return 0;
}

虽然我在上课的时候能听懂费老师对于每道题贪心策略的讲解,但是自己单独的解决一道贪心的问题就要费很大的劲,我之后要对贪心的题目多加练习,才能熟练地掌握这类问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值