chap4.贪心算法

活动安排O(nlogn)

活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合,是可以用贪心算法有效求解的很好例子。该问题要求高效地安排一系列争用某一公共资源的活动。贪心算法提供了一个简单、漂亮的方法使得尽可能多的活动能兼容地使用公共资源。
设有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相容。

通过对数组进行按结束时间从小到大排序,每次按顺序贪心即可

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
struct act
{
  int s;
  int f;
};
int n;
act d[1000];
int choose[1000];
void solve()
{
  choose[1]=1;
  int pos=1;
  for(int i=2;i<=n;i++)
  {
    if(d[i].s>=d[pos].f)
    {
      choose[i]=1;
      pos=i;
    }
    else choose[i]=0;
  }
}
int cmp(act a,act b)
{
  if(a.f==b.f)
    return a.s<b.s;
  else return a.f<b.f;
}
int main()
{
  srand(time(0));
  cin>>n;
  for(int i=1;i<=n;i++)
  {
    d[i].s=rand()%25;
    d[i].f=d[i].s+rand()%20;
    //cout<<" i: "<<i<<" s: "<<d[i].s<<" f: "<<d[i].f<<endl;
  }
  sort(d+1,d+1+n,cmp);
  for(int i=1;i<=n;i++)
  {
   
    cout<<" i: "<<i<<" s: "<<d[i].s<<" f: "<<d[i].f<<endl;
  }
  solve();
  cout<<"ans: "<<endl;
  for(int i=1;i<=n;i++)
  {
    if(choose[i])
    {
      cout<<" i: "<<i<<" s: "<<d[i].s<<" f: "<<d[i].f<<endl;
    }  
  }
}

贪心算法的基本要素

贪心算法的两个重要性质(验证贪心算法的正确性需要证明这两个性质满足):

1.贪心选择性质(自顶向下)证明:找一个选择方案假设其为最优方案,然后将方案和贪心的方案交换,看看还满不满足最优方案。

2.最优子结构性质 证明:看其子结构能不能推导出当前解,如果有更优解就不能推出当前解,不满足最优性质

与动态规划区别:不能对重叠子问题进行解,如0-1背包和背包问题

最优装载O(nlogn):

有一批集装箱要装上一艘载重量为c的轮船。其中集装箱i的重量为Wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

采用贪心思路,将货物由小到大排序,一个个按顺序装。

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
struct box
{
  int id;
  int w;
};
int n;
int c;
box d[1000];
int choose[1000];
void solve()
{
  for(int i=1;i<=n;i++)
  {
    if(c<d[i].w)break;
    choose[d[i].id]=1;
    c-=d[i].w;
  }
}
int cmp(box a,box b)
{
  return a.w<b.w;
}
int main()
{
  srand(time(0));
  cin>>n;
  for(int i=1;i<=n;i++)
  {
    d[i].id=i;
    d[i].w=rand()%50;
  }
  c=rand()%200;
  cout<<"c: "<<c<<endl;
  sort(d+1,d+1+n,cmp);
  for(int i=1;i<=n;i++)
  {
    cout<<"id: "<<d[i].id<<" w: "<<d[i].w<<endl;
  }
  solve();
  for(int i=1;i<=n;i++)
  {
    if(choose[i])cout<<i<<" ";
  }
}

哈夫曼编码O(nlogn)

变长码:就是每次取最小的两个合并成一个,直到仅剩一个位置,其为根。(最优)

定长码:每次两两组合各自合并。

 

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
struct huffman
{
  char id;
  int w;
  huffman*l;
  huffman*r;
};
int n;
char c[100];
map<char,int>f;
struct cmp
{
  bool operator()(const huffman* a,const huffman* b)
  {
    return a->w>b->w;
  }
};
huffman*solve()
{
  priority_queue<huffman*,vector<huffman*>,cmp>q;
  for(int i=1;i<=26;i++)
  {
    huffman*t=new huffman();
    t->w=f[c[i]];
    t->id=c[i];
    q.push(t);
  }
  while(q.size())
  {
    if(q.size()==1)break;

    huffman*a=q.top();q.pop();
    huffman*b=q.top();q.pop();
    cout<<"debug: a:"<<a->w<<" b: "<<b->w<<endl;
    huffman*newhuffman=new huffman();
    newhuffman->l=a;
    newhuffman->r=b;
    newhuffman->w=a->w+b->w;
    q.push(newhuffman);
  }
  return q.top();
}
void show(huffman*tree,string xu)
{
  if(tree->l==NULL&&tree->r==NULL)
  {
    cout<<tree->id<<" : "<<xu<<endl;
    return;
  }
  show(tree->l,xu+"0");
  show(tree->r,xu+"1");
}
int main()
{
  srand(time(0));
  
  for(int i=1;i<=26;i++)
  {
   c[i]='a'+i-1;
   f[c[i]]=rand()%50;
   cout<<c[i]<<" : "<<f[c[i]]<<endl;
  }
  //f['z']=2000;
  huffman*tree=solve();
  show(tree,"");
}

单源最短路径(dijkstra)普通O(n2),优先队列O(nlogn)

每次取出当前距离最短的,标记已走过(已是到该点最短距离),遍历每个相邻未访问点,若距离小于目标点距离,则更新并放入队列

 意思就是如果要得出dist[u],那么优先队列肯定会遍历x点,那么就一定会找到那个更短的路,这样依然是最短路。

 

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
int n;
int dist[1000];
vector<int>d[1000];
int len[1000][1000];
int pre[1000];
int vis[1000];
int inf=100000;
void solve(int v)
{
  
  memset(dist,0x3f3f,sizeof(dist));
  for(int i=1;i<=n;i++)//初始化
  {
    dist[i]=len[i][v];
    vis[i]=0;
    if(dist[i]==inf)
    {
      pre[i]=0;
    }
    else pre[i]=v;
  }
  dist[v]=0;
  vis[v]=1;
  for(int i=1;i<n;i++)//找到当前到源最短距离的点
  {
    int temp=inf;
    int u=v;
    for(int j=1;j<=n;j++)
    {
      if(!vis[j]&&dist[j]<temp)
      {
        temp=dist[j];
        u=j;
      }
    }
    vis[u]=1;
    for(int j=1;j<=n;j++)
    {
      if(!vis[j]&&len[u][j]!=inf)//三角形法则更新最短距离
      {
        if(dist[u]+len[u][j]<dist[j])
        {
          dist[j]=dist[u]+len[u][j];
          pre[j]=u;
        }
      }
    }
  }
}
int main()
{
  srand(time(0));
  cin>>n;
  for(int i=1;i<=n;i++)
    len[i][i]=inf;
  for(int i=1;i<=n;i++)
  {
    for(int j=i+1;j<=n;j++)
    {
      d[i].push_back(j);
      d[j].push_back(i);
      int lenn=rand()%50;
      len[i][j]=lenn;
      cout<<"i: "<<i<<" j: "<<j<<" len: "<<lenn<<endl;
      len[j][i]=lenn;
    }
  }
  solve(1);
  int now=n;
  cout<<"ans: "<<dist[n]<<endl;
  while(now!=1)
  {
    cout<<now<<" ";
    now=pre[now];
  }
}

 

最小生成树

利用并查集,按边长从小到大排序,每次将不在集合内的一个点放入集合(该边最小)。

kruskalO(eloge)

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
int n;
int f[1000];
int findfa(int x)
{
  if(x==f[x])return  x;
  else return f[x]=findfa(f[x]);
}
struct edge
{
  int u;
  int v;
  int w;
};
vector<edge>e;
vector<int>ans;
int cmp(edge a,edge b)
{
  return a.w<b.w;
}
void solve()
{
  for(int i=1;i<=n;i++)
  {
    f[i]=i;
  }
  for(int i=0;i<e.size();i++)
  {
    int u,v;
    u=e[i].u;
    v=e[i].v;
    int fu=findfa(u);
    int fv=findfa(v);
    if(fu!=fv)
    {
      f[fu]=fv;
      ans.push_back(i);
    }
  }
}
int main()
{
  srand(time(0));
  cin>>n;
  for(int i=1;i<=n;i++)
  {
    for(int j=i+1;j<=n;j++)
    {
      e.push_back({i,j,rand()%50});
    }
  }
  sort(e.begin(),e.end(),cmp);
  solve();
  for(int i=0;i<e.size();i++)
  {
    cout<<"u: "<<e[i].u<<" v: "<<e[i].v<<" w: "<<e[i].w<<endl;
  }
  for(int i=0;i<ans.size();i++)
  {
    cout<<i<<" ";
  }
}

prim 普通O(n2)优先队列O(nlogn)

每次队列放入该集合相邻的所有边,然后每次放入未进入集合的点中最短边

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
int n;
int vis[1000];
vector<int>d[1000];
int len[1000][1000];
struct edge
{
  int t;
  int w;
  bool operator<(const edge&a)const
  {
    return w<a.w;
  }
};

void solve()
{
  int sum=0;
  priority_queue<edge>q;
  vis[1]=1;
  int cnt=1;
  for(int i=0;i<d[1].size();i++)
  {
    q.push({d[1][i],len[1][d[1][i]]});
  }
  while(q.size())
  {
    if(cnt==n)break;
    edge t=q.top();
    q.pop();
    if(vis[t.t])continue;
    vis[t.t]=1;
    cnt++;
    sum+=t.w;
    for(int i=0;i<d[t.t].size();i++)
    {
      if(!vis[d[t.t][i]])
      q.push({d[t.t][i],len[t.t][d[t.t][i]]});
    }
  }
  cout<<sum<<endl;

}
int main()
{
  srand(time(0));
  cin>>n;
  for(int i=1;i<=n;i++)
  {
    for(int j=i+1;j<=n;j++)
    {
      d[i].push_back(j);
      d[j].push_back(i);
      int lenn=rand()%50;
      len[i][j]=lenn;
      len[j][i]=lenn;
      cout<<"i: "<<i<<" j: "<<j<<" w: "<<len[i][j]<<endl;
    }
  }
  solve();
  
}

多机调度(NP问题,没有有效解法)O(nlogn)

m台机器,每次将最大的作业放入当前耗时最少的机器内。

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
int n,m;
int ti[1000];
void solve()
{
  priority_queue<int,vector<int>,greater<int>>q;
  for(int i=0;i<m;i++)
  {
    q.push(0);
  }
  sort(ti+1,ti+1+n);
  for(int i=1;i<=n;i++)
  {
    //ti[i]=rand()%50;
    cout<<ti[i]<<" ";
  }
  cout<<endl;
  for(int i=n;i>=1;i--)
  {
    int tt=q.top();
    q.pop();
    tt+=ti[i];
    q.push(tt);
  }
  while(q.size())
  {
    cout<<q.top()<<endl;
    q.pop();
  }
}
int main()
{
  srand(time(0));
  cin>>n>>m;
  for(int i=1;i<=n;i++)
  {
    ti[i]=rand()%50;
    //cout<<ti[i]<<" ";
  }
  cout<<endl;
  solve();
  
  
}

 

posted @ 2023-07-09 22:16  hbhhbh  阅读(24)  评论(0)    收藏  举报