专题二:贪心与最小生成树
贪心:
贪心算法总是做出在当前看来最优的选择,也就是贪心算法不从整体最优考虑,他做出的选择只是在某种意义上的局部最优的选择。
贪心是自顶而下的,从局部出发解决问题。而动态规划考虑子问题的重叠,自底而上,倒推的代码比较好写,重要的是写出状态转移方程。
例题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;
}