greedy算法/算法导论/挑战程序设计竞赛
贪心算法自顶向下,常规递归-》尾递归-》迭代。每次看一个解。
贪心算法的选择:
1.一个全局最优解可以通过局部最优(贪心)来选择达到。
2.我们所做的选择总是当前看起来最佳的的选择,然后再解决选择之后出现的子问题,可能依赖已经作出的选择但是不会依赖还没有做出选择的子问题。
3.证明在每一步所做的贪心选择最终能产生一个全局最优解。
结果:我们先证明考察一个全局最优解,然后证明可以对该解加以修改,使其采用贪心选择,这个选择将原问题变为一个相似的,但更小的问题。
贪心算法能解决部分背包问题,不能解决0-1背包问题;
因为部分背包问题是可拆解的:查看
贪心算法的递归版:
1 RECURSIVE_ACTIVEITY_SELECTIOR(s,f,i,n) 2 { 3 m=i+1; 4 while(m<=n && s[m]<f[i]) m++; 5 if(m<=n) 6 return {a[m]} 并 RECURSIVE_ACTIVEITY_SELECTOR(s,f,m,n); 7 else return 空 8 }
贪心算法的迭代版:
1 GREEDY_ACTIVITY_SELECTOR(s,f) 2 { 3 n=length[s]; 4 A<-{a1}; 5 i=1; 6 for(int m=2;m<=n;m++) 7 if(s[m]>f[i]) 8 { 9 A=A并a[m]; 10 i=m; 11 } 12 return A; 13 }
应用之一:
coin change/硬币问题
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 6 const int v[6]={1,5,10,50,100,500}; 7 8 int main() 9 { 10 int c[6]; 11 for(int i=0;i<6;i++) 12 scanf("%d",&c[i]); 13 14 int ans=0,A; 15 scanf("%d",&A); 16 17 for(int i=5;i>=0;i--) 18 { 19 int t=min(A/v[i],c[i]); 20 A-=t*v[i]; 21 ans+=t; 22 } 23 printf("%d\n",ans); 24 return 0; 25 }
区间调度问题,也就是活动选择问题的泛化原型。
哎,我们总是会想哦,活动选择问题它给了你开始时间,结束时间,然后就是活动运行的时间,然后就是活动重叠的个数(少的话可以选)
那么我们选择贪心策略的时候该怎么去选择了,也就是说一个问题里面最突出的特征我们就要去尝试用贪心去解决它,所以我们以上的三种考虑只有第一种是对的~
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 #define maxn 100005 7 8 pair<int,int> pq[maxn]; //简单的可以不用priority_queue,pair的sort总是先对first值排序再对second排序。 9 10 int main() 11 { 12 int f[maxn],s[maxn]; 13 int n; 14 scanf("%d",&n); 15 for(int i=1;i<=n;i++) 16 scanf("%d",&s[i]); 17 for(int i=1;i<=n;i++) 18 scanf("%d",&f[i]); 19 for(int i=1;i<=n;i++) 20 { 21 pq[i].first=f[i]; 22 pq[i].second=s[i]; 23 } 24 sort(pq,pq+n); 25 int ans=0; 26 int t=0; 27 for(int i=1;i<=n;i++) 28 { 29 if(t<pq[i].second) 30 { 31 t=pq[i].first; 32 ans++; 33 } 34 } 35 printf("%d\n",ans); 36 return 0; 37 38 }
字典序最小问题:
这是书上的代码感觉冗余了。
View Code自己写的:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 6 using namespace std; 7 #define maxn 2005 8 9 int main() 10 { 11 char s[maxn]; 12 13 int n; 14 gets(s); 15 int l=0,r=strlen(s)-1; 16 while(l<=r) 17 { 18 if(s[l]<s[r]) 19 { 20 putchar(s[l++]); 21 } 22 else 23 { 24 putchar(s[r--]); 25 } 26 } 27 puts(""); 28 return 0; 29 }
其他例题:区间覆盖点问题:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 #define maxn 1005 7 8 int main() 9 { 10 int x[maxn]; 11 int n,r; 12 scanf("%d%d",&n,&r); 13 14 for(int i=0;i<n;i++) 15 scanf("%d",&x[i]); 16 sort(x,x+n); 17 int ans=0,t=0,i=0; 18 while(i<n) 19 { 20 while(i<n && x[t]>=x[i]-r) i++; 21 t=i-1; 22 while(i<n && x[i]<=x[t]+r) i++; 23 t=i; 24 ans++; 25 } 26 printf("%d\n",ans); 27 return 0; 28 }
不过书上的思想简单易懂。它只往一个方向找,我是往前往后看。。。似乎有点违背贪心的原理了,极度简化动态规划的繁琐感~
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 #define maxn 1005 7 8 int main() 9 { 10 int x[maxn]; 11 int n,r; 12 scanf("%d%d",&n,&r); 13 14 for(int i=0;i<n;i++) 15 scanf("%d",&x[i]); 16 sort(x,x+n); 17 int i=0,ans=0; 18 while(i<n) 19 { 20 int s=x[i++]; 21 while(i<n && x[i]<=s+r) i++; 22 int p=x[i-1]; 23 while(i<n && x[i]<=p+r) i++; 24 ans++; 25 } 26 printf("%d\n",ans); 27 return 0; 28 }
求最小开销问题:
huffman code写过了fence repair 复杂度为O(n*logn)
1 #include <iostream> 2 #include <cstdio> 3 #include <queue> 4 #include <algorithm> 5 6 7 using namespace std; 8 #define maxn 20005 9 struct node 10 { 11 int w; 12 node(){} 13 node(int w):w(w){} 14 bool operator<(const node& a)const 15 { 16 return w>a.w; 17 } 18 }; 19 20 21 priority_queue<node> pq; 22 23 int main() 24 { 25 int n; 26 int l[maxn]; 27 scanf("%d",&n); 28 for(int i=0;i<n;i++) 29 { 30 scanf("%d",&l[i]); 31 pq.push(node(l[i])); 32 } 33 34 int ans=0; 35 node now; 36 int a,b; 37 while(pq.size()>1) 38 { 39 a=pq.top().w;pq.pop(); 40 b=pq.top().w;pq.pop(); 41 now.w=a+b; 42 pq.push(now); 43 ans+=now.w; 44 } 45 printf("%d\n",ans); 46 return 0; 47 }
好,我们开始做题目了。
数据过小,用暴力搜一下。
uva,102
1 #include <iostream> 2 #include <cstdio> 3 #include <string> 4 5 using namespace std; 6 7 char s[6][4]={ //想过这个思想 8 "BCG","BGC","CBG", 9 "CGB","GBC","GCB" 10 }; 11 12 int table[6][9]={ 13 0,1,1, 1,1,0, 1,0,1, 14 0,1,1, 1,0,1, 1,1,0, 15 1,1,0, 0,1,1, 1,0,1, 16 1,1,0, 1,0,1, 0,1,1, 17 1,0,1, 0,1,1, 1,1,0, 18 1,0,1, 1,1,0, 0,1,1 19 }; 20 21 int data[9]; 22 23 int main() 24 { 25 while(~scanf("%d",&data[0])) 26 { 27 int Min=0,spa=1; 28 for(int i=1;i<9;i++) 29 { 30 scanf("%d",&data[i]); 31 Min+=data[i]; 32 } 33 34 for(int i=0;i<6;i++) 35 { 36 int sum=0; 37 for(int j=0;j<9;j++) 38 sum+=data[j]*table[i][j]; 39 40 if(Min>sum) 41 { 42 Min=sum; 43 spa=i; 44 } 45 } 46 printf("%s %d\n",s[spa],Min); 47 } 48 return 0; 49 }
uva,10020
区间/工作调度的变形问题,设s[i]和f[i]分别是任务开始的时间和结束的时间。
首先排序,然后按照把需要求的区间分为三段,然后分段管理,如果下个值的s[i]>f[i-1]则可选择达成此条件的f[i]的最大值去覆盖,应用了贪心的策略,使得最小。每次找出如果最后m>0则我们最后就无法覆盖。
一开始还只是一个想法,但是没想到想着想着就写出来了。😄
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 #define maxn 100005 7 8 struct node 9 { 10 int l,r; 11 node(){} 12 node(int l,int r):l(l),r(r){} 13 }; 14 pair<int,int> pq[maxn]; 15 16 vector<node> vt; 17 int main() 18 { 19 int t; 20 scanf("%d",&t); 21 int l,r; 22 int m; 23 while(t--) 24 { 25 vt.clear(); 26 int flag1=0,flag2=0,flag3=0,flag4=0; 27 int cnt=0,ans=0; 28 scanf("%d",&m); 29 while(~scanf("%d%d",&l,&r)) 30 { 31 if(!l && !r) 32 break; 33 if(r<0 || l>m) 34 continue; 35 pq[cnt].first=l; 36 pq[cnt++].second=r; 37 } 38 sort(pq,pq+cnt); 39 if(cnt==0) flag1=1; 40 int i=0,maxx=0,index=0; 41 while(i<cnt && pq[i].first<=0) 42 { 43 if(maxx<pq[i].second) 44 { 45 maxx=pq[i].second; 46 index=i; 47 } 48 if(maxx>=m) 49 { 50 ans=1; 51 flag4=1; 52 vt.push_back(node(pq[i].first,pq[i].second)); 53 m=-1; 54 break; 55 } 56 i++; 57 } 58 if(maxx<=0) 59 { 60 maxx=0; 61 flag2=1; 62 } 63 if(!flag2 && maxx<m) 64 { 65 vt.push_back(node(pq[index].first,pq[index].second)); 66 m-=maxx; 67 ans=1; 68 } 69 int maxxx=0; 70 index=0; 71 while(i<cnt && pq[i].first>=0 && !flag4 && m>=0) 72 { 73 while(pq[i].first<=maxx && i<cnt) 74 { 75 flag3=1; 76 if(maxxx<pq[i].second) 77 { 78 maxxx=pq[i].second; 79 index=i; 80 } 81 i++; 82 } 83 if(flag3) 84 { 85 int maxxxx=maxxx; 86 maxxx-=maxx; 87 maxx=maxxxx; 88 m-=maxxx; 89 vt.push_back(node(pq[index].first,pq[index].second)); 90 ans++; 91 } 92 else 93 break; 94 } 95 if(flag4 && m<=0) 96 { 97 printf("1\n"); 98 printf("%d %d\n",vt[0].l,vt[0].r); 99 } 100 else if(!flag4 && m<=0) 101 { 102 printf("%d\n",ans); 103 for(int i=0;i<vt.size();i++) 104 { 105 printf("%d %d\n",vt[i].l,vt[i].r); 106 } 107 } 108 else 109 printf("0\n"); 110 if(t>0) 111 puts(""); 112 113 } 114 return 0; 115 116 }
uva,10714
针对最小的掉下去的时间,我们可以找出每只蚂蚁离两端最近的去跑,这样同时也可以保证不撞,然后求出最小值里面的最大值。
针对最大的掉下去的时间,题目给的是两只蚂蚁相撞之后会掉头,其实走的路程就是两只蚂蚁穿过彼此继续走而已。
就是最小的反例,然后取最大的即为所求。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <vector> 5 6 using namespace std; 7 #define maxn 1000005 8 9 int main() 10 { 11 int t; 12 int len,n; 13 int dis[maxn]; 14 scanf("%d",&t); 15 while(t--) 16 { 17 scanf("%d%d",&len,&n); 18 19 for(int i=0;i<n;i++) 20 scanf("%d",&dis[i]); 21 sort(dis,dis+n); 22 23 int low=0,top=0; 24 25 for(int i=0;i<n;i++) 26 { 27 if(dis[i]<=len/2) 28 { 29 if(low<dis[i]) 30 low=dis[i]; 31 } 32 else 33 { 34 if(low<(len-dis[i])) 35 low=len-dis[i]; 36 } 37 } 38 39 for(int i=0;i<n;i++) 40 { 41 if(dis[i]<=len/2) 42 { 43 if(top<(len-dis[i])) 44 top=len-dis[i]; 45 } 46 else 47 { 48 if(top<dis[i]) 49 top=dis[i]; 50 } 51 } 52 printf("%d %d\n",low,top); 53 54 } 55 return 0; 56 }
uva,11100
其实思想自己做了两种方法,但是都越界了,不明觉厉。
其实就是排序之后找上升子序列,那个重复最多的数代表最后会留下几个包。
注意不一定是最大上升子序列,其实看了网上的一些题解后发现要求每个留下包的里面的包尽量平均。
其实最简单的最让人感觉无奈的是。。。。其实找出最后留几个包,然后等差去扫就可以。哇哦,这么简单。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 #define maxn 10005 7 8 int main() 9 { 10 int n,A[maxn]; 11 while(scanf("%d",&n),n) 12 { 13 for(int i=0;i<n;i++) 14 scanf("%d",&A[i]); 15 16 sort(A,A+n); 17 int t=1,k=0; 18 19 for(int i=0;i<n;i++) 20 { 21 if(A[i-1]==A[i]) t++; 22 else 23 { 24 k=max(t,k); 25 t=1; 26 } 27 } 28 k=max(k,t); 29 30 31 printf("%d\n",k); 32 for(int i=0;i<k;i++) 33 { 34 int first=1; 35 for(int j=i;j<n;j+=k) 36 { 37 if(!first) printf(" "); 38 printf("%d",A[j]); 39 if(first) first=0; 40 } 41 printf("\n"); 42 } 43 } 44 return 0; 45 }
uva,11157
需要跳来回的考虑用两只去跳,需要相撞走来回路的交换两只动物,需要贪心的排序。
我一开始写了一下午,都感觉没用贪心法的思路去做,。。。。。。哭。
我来回跳,我一开始就选两只青蛙一起跳,都能到的最大里面去最下就可以了。
大的石头都能跳,小的石头轮流跳,然后更小最大最小值。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 7 #define maxn 105 8 9 struct node 10 { 11 int w; 12 int cnt; 13 node(){} 14 node(int w,int cnt):w(w),cnt(cnt){} 15 }p[maxn]; 16 17 int main() 18 { 19 int t; 20 int n,M; 21 scanf("%d",&t); 22 for(int k=1;k<=t;k++) 23 { 24 scanf("%d%d",&n,&M); 25 char s1,s2; 26 int s; 27 for(int i=1;i<=n;i++) 28 { 29 cin>>s1>>s2>>s; 30 if(s1=='S') 31 p[i]=node(s,1); 32 else 33 p[i]=node(s,maxn); 34 } 35 p[0]=node(0,1),p[n+1]=node(M,maxn); 36 37 38 int pre1=0,pre2=0; 39 int now1=0,now2=0; 40 int maxmin=0; 41 for(int i=1;i<=n+1;i++) 42 { 43 if(p[i].cnt>1) 44 { 45 now2=now1=p[i].w; 46 if(now1-pre1>maxmin) maxmin=now1-pre1; 47 if(now2-pre2>maxmin) maxmin=now2-pre2; 48 pre1=now1;pre2=now2; 49 } 50 else 51 { 52 now1=p[i].w; 53 if(pre1<pre2) 54 { 55 if(now1-pre1>maxmin) 56 maxmin=now1-pre1; 57 pre1=now1; 58 } 59 else 60 { 61 if(now1-pre2>maxmin) 62 maxmin=now1-pre2; 63 pre2=now1; 64 } 65 } 66 67 } 68 printf("Case %d: %d\n",k,maxmin); 69 } 70 return 0; 71 72 }

浙公网安备 33010602011771号