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 return8 }
View Code

 

贪心算法的迭代版:

 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 }
View Code

 

 

应用之一:

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 }
View Code

区间调度问题,也就是活动选择问题的泛化原型。

 

哎,我们总是会想哦,活动选择问题它给了你开始时间,结束时间,然后就是活动运行的时间,然后就是活动重叠的个数(少的话可以选)

那么我们选择贪心策略的时候该怎么去选择了,也就是说一个问题里面最突出的特征我们就要去尝试用贪心去解决它,所以我们以上的三种考虑只有第一种是对的~

 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

 

字典序最小问题:

这是书上的代码感觉冗余了。

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 }
View Code

 

 

其他例题:区间覆盖点问题:

 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 }
View Code

 

求最小开销问题:

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 }
View Code

 

好,我们开始做题目了。

数据过小,用暴力搜一下。

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

posted @ 2016-04-27 20:25  指尖泛出的繁华  阅读(301)  评论(0)    收藏  举报