17寒假集训_贪心与最小生成树

专题二:贪心与最小生成树

贪心:
贪心算法总是做出在当前看来最优的选择,也就是贪心算法不从整体最优考虑,他做出的选择只是在某种意义上的局部最优的选择。
贪心是自顶而下的,从局部出发解决问题。而动态规划考虑子问题的重叠,自底而上,倒推的代码比较好写,重要的是写出状态转移方程。

例题1:
活动规划:
问题表述:设有n个活动的集合E = {1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si < fi 。如果选择了活动i,则它在半开时间区间[si, fi)内占用资源。若区间[si, fi)与区间[sj, fj)不相交,则称活动i与活动j是相容的。也就是说,当si >= fj或sj >= fi时,活动i与活动j相容。
https://vjudge.net/contest/149987#problem/A
HDU 2037 今年暑假不AC
用结构体保存start和end 对end进行排序,从头遍历,符合条件的则ans++

#include <iostream>
#include <cstdio>
#include <sstream>
#include <set>
#include <bitset>  
#include <queue>  
#include <stack>
#include <list>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef set<int> Set;
typedef vector<int> Vec;
typedef set<int>::iterator It;

#define mem(s,n) memset(s,n,sizeof(s))
#define SZ(v) (v.size())

struct time{
    int start,end;
    bool operator < (time x)const
    {
        return end < x.end;
    }
} a[105];

int main(int argc, char *argv[])
{
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        for(int i=0;i<n;i++)
            scanf("%d%d",&a[i].start,&a[i].end);
        sort(a,a+n);            
        int num=1,End=a[0].end;
        for(int i=0;i<n-1;i++)
        {
            if(End<=a[i+1].start)
            {
                num++;
                End=a[i+1].end;
            }
        }
        printf("%d\n",num);
    }
    return 0;
}

作为例题,很多活动规划类题型都可以由这个拓展而来
HDU 1051https://vjudge.net/contest/149987#problem/C
木筷加工,后一个木筷的length和weight都比前一个加工的大,则无费用,否则费用+1
解题思路:对木筷进行排序,如例1 用vector储存木筷,当所有木筷都被加工后跳出循环。

#include <iostream>
#include <cstdio>
#include <sstream>
#include <set>
#include <bitset>  
#include <queue>  
#include <stack>
#include <list>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef set<int> Set;
typedef vector<int> Vec;
typedef set<int>::iterator It;

#define mem(s,n) memset(s,n,sizeof(s))
#define SZ(v) ((int)v.size())

struct St{
    int l,w;
    bool operator < (const St &temp) const
    {
        return (l!=temp.l)? l<temp.l : w<temp.w;
    }
};

int main(int argc, char *argv[])
{
    vector<St> a(5005);
    int kase;
    scanf("%d",&kase);
    while(kase--)
    {
        int n,n0;
        scanf("%d",&n);
        a.resize(n);
        n0=n;
        int x,y;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].w);
        }
        sort(a.begin(),a.end());
        int ans=0;
        while(!a.empty())
        {
            ans++;
            St s=a[0];
            a.erase(a.begin());
            for(int i=0;i<SZ(a);i++)
            {
                if(a[i].l>=s.l && a[i].w>=s.w)
                {
                    s=a[i];
                    a.erase(a.begin()+i);
                    i--;
                }               
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

最小生成树:
即最小权值生成树的简称
用一个例题介绍Prim和Kruskal算法
POJ 1251 https://vjudge.net/contest/149987#problem/G
Sample Input
9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0
Sample Output
216
30

hint:8个村庄,每行开头字母表示村庄标号,后面跟一个数字表示这个村庄和几个村相连

Prim算法
#include <cstdio>
#include <cstring>
using namespace std;
#define N 100
#define M 10000

int mmap[N][N];
int vis[N];//访问标记
char s[2],to[2];

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        int t,m;
        memset(mmap,-1,sizeof(mmap));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n-1;i++)
        {
            scanf("%s %d",s,&m);
            while(m--)
            {
                scanf("%s %d",to,&t);
                int u = s[0]-'A',v = to[0]-'A';
                mmap[u][v] = mmap[v][u] = t;
            }
        }
        //prim
        int ans=0;
        m=0;
        vis[0] = 1;
        while(m < n-1)
        {
            int mmin=0x3f3f3f3f,u=-1;
            for(int i=0;i<n;i++)
                if(vis[i])
                    for(int j=0;j<n;j++)
                        if(!vis[j] && mmap[i][j] != -1 && mmap[i][j] < mmin)
                        {
                            mmin = mmap[i][j];
                            u=j;
                        }
            if(mmin != 0x3f3f3f3f)
            {
                m++;
                vis[u] = 1;
                ans += mmin;
            }
            else
                break;
        }

        if(m==n-1)
            printf("%d\n",ans);
        else
            printf("0\n");//无解
    }
    return 0;
}
kruskal算法
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100
#define M 10000
struct edge
{
    int u,v,val;//u点连接到v点的无向边权值是val
}E[M];
int e=0;//边计数器

int F[N];

bool cmp(edge a,edge b)//排序所用的比较函数
{
    return a.val<b.val;
}

int findf(int x)//并查集操作
{
    if(F[x] == -1)
        return x;
    return F[x]=findf(F[x]);
}

char s[2],to[2];

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        int t,m;
        memset(E,-1,sizeof(E));
        memset(F,-1,sizeof(F));
        e=0;
        for(int i=0;i<n-1;i++)
        {
            scanf("%s %d",s,&m);
            while(m--)
            {
                scanf("%s %d",to,&t);
                E[e].u=s[0]-'A';
                E[e].v=to[0]-'A';
                E[e++].val=t;
            }
        }
        //Kruskal
        int ans=0;
        m=0;
        sort(E,E+e,cmp);//排序
        for(int i=0;i<e;i++)
        {
            int u=E[i].u;
            int v=E[i].v;
            int w=E[i].val;
            if(findf(u)!=findf(v))
                ans+=w,F[findf(u)]=findf(v),m++;//合并
            if(m==n-1)
                break;
        }

        if(m==n-1)
            printf("%d\n",ans);
        else
            printf("0\n");//无解
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值