程序设计思维作业 week3

程序设计思维作业 week3

本周主要练习的内容是贪心算法,主要的找到贪心准则。整体做题体验前两题非常顺利,每道题20分钟左右,第三题在降低复杂度方面花费了较多时间。

Problem A 选数问题

Given N positive numbers, you should select K of them that sum to S. Now please calculate how many ways to get it!

1.sample input and output

Input

The first line, an integer T<=100, indicates the number of test cases. For each case, there are two lines. The first line, three integers indicate n, K and S.The second line n integers indicate the positive numbers.

1
10 3 10
1 2 3 4 5 6 7 8 9 10

Output

For each case, an integer indicate the answer in a independent line.

4

2.整体思路及代码

对输入数组进行遍历,对于每一个元素都有选和不选两个选项。如果选,用与目标和的差值减去该元素,然后看下一个元素。如果不选,直接进入下一个元素。其中及时终止条件(可行性剪枝)是与目标和的差值小于0,或选取数的数目超过K个却还不到目标和S。

#include<iostream>
#include<algorithm>
using namespace std;

int tmp;
int n,m,K,S;
int *p;
void SOL(int i,int size,int sum)
{
    //符合K个数相加等于S的要求
    if(sum==0&&size==K)
    {
        tmp++;
        return ;
    }
    //提前结束情况
    if(sum<0||size>K||i>=m)
        return ;
    //如果选第i个数
    SOL(i+1,size+1,sum-p[i]);
    //不选
    SOL(i+1,size,sum);
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        tmp=0;
        //一共m个数,K个数的和为S的方案个数
        cin>>m;
        cin>>K;
        cin>>S;
        p=new int[m];
        for(int j=0;j<m;j++)
        {
            cin>>p[j];
        }
        SOL(0,0,S);
        cout<<tmp<<endl;
    }
}

Problem B 区间选点问题

数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

1.sample input and output

Input

第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)

2
1 5
4 6

Output

一个整数,代表选点的数目

1

2.整体思路及代码

经典贪心问题。先将输入的区间按右端点由小到大排序,用lim记录当前选中区间的右端点值。然后遍历区间数组,如果lim点在当前区间中,continue到下一区间。如果不在,则将该区间右端点赋值给lim,并选取区间数++。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class section
{
public:
    section()
    {
        _a=0;
        _b=0;
    }
    section(int a,int b)
    {
        _a=a;
        _b=b;
    }
    inline bool operator < ( section & x)
    {
        return _b<x._b;
    }
public:
    int _a,_b;
};

bool cmp(section a,section b)
{
    return a._b<b._b;
}

int n;
vector<section> p;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int a,b;
        cin>>a;
        cin>>b;
        section Nsection(a,b);
        p.push_back(Nsection);
    }
    sort(p.begin(),p.begin()+n,cmp);
    int lim=p.front()._b;
    int tmp=1;
    for(int i=1;i<n;i++)
    {
        if(lim>=p[i]._a&&lim<=p[i]._b)
            continue;
        else
        {
            tmp++;
            lim=p[i]._b;
        }
    }
    cout<<tmp<<endl;
    return 0;
}

Problem C 区间覆盖问题

数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1

1.sample input and output

Input

第一行:N和T
第二行至N+1行: 每一行一个闭区间。

3 10
1 7
3 6
6 10

Output

选择的区间的数目,不可能办到输出-1

2

2.整体思路及代码

贪心问题,先上一张解题心路历程图。
C题递交过程

思路设计方面,先将输入的区间数组按照右端点排序。用mlim来记录当前选中区间的最右端点数值,用pos来记录当前选中区间在数组中的位置。每次调用count函数,会从pos往后遍历数组,选取从milm开始,并且右端点值最大的区间,将新的右端点值赋给Mlim,记录pos。直到到达目标右端点值t。其中,无法实现的情况有,区间与区间之间有无法覆盖的空点(体现在代码中是Mlim没有被赋予新值),最大右端点无法到达目标值t(体现在代码中是start等于Mlim)。
最终代码复杂度为O(nlogn)

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

class section
{
public:
    section()
    {
        _a=0;
        _b=0;
    }
    section(int a,int b)
    {
        _a=a;
        _b=b;
    }
public:
    int _a,_b;
};

int n;//区间个数
int t;//目标右端点值
int wa=-1;//错误输出
int tmp=1;//区间个数计数
vector<section> p;//区间数组

bool cmp(section a,section b)
{
    return a._b<b._b;
}

void count(int s,int start)
{
    //记录从start开始的区间能够到达的最右点
    int Mlim=0;
    //记录下一次数组遍历的起始位置
    int pos=0;
    //判断是否成功结束
    if(start==t)
    {
        cout<<tmp;
        return;
    }
    //简化区间到【start,t】之中
    for(int i=s+1;i<p.size();i++)
    {
        if(p[i]._a<=start+1)
        {
            //p[i]._a = start;
            if(p[i]._b>Mlim)
            {
                Mlim = p[i]._b;
                pos=i;
            }
        }
    }
    if(Mlim==0||Mlim==start)
    {
        cout<<wa;
        return ;
    }
    else {
        tmp++;
        //cout<<Mlim<<endl;
        count(pos,Mlim);
    }
}

int main()
{
    while(cin>>n)
    {
        scanf("%d", &t);
        //记录从1起始的区间能够到达的右极点
        tmp=1;
        int mlim = 0;
        for (int i = 0; i < n; i++) {
            int a, b;
            scanf("%d", &a);
            scanf("%d", &b);
            if (b >= t) {
                b = t;
            }
            if (a <= 1) {
                a = 1;
                if (b > mlim)
                    mlim = b;
            }
            section Nsection(a, b);
            p.push_back(Nsection);
        }
        if (mlim == 0) {
            cout << wa;
            return 0;
        }
        //按右端点排序
        sort(p.begin(), p.begin() + p.size(), cmp);
        count(0, mlim);
        p.clear();
    }
    return 0;
}
发布了8 篇原创文章 · 获赞 2 · 访问量 252

猜你喜欢

转载自blog.csdn.net/lawrenceY/article/details/104761358
今日推荐