[贪心]

一、区间调度问题

有n项工作,每项工作分别在a[i].begin开始,在a[i].end结束,求最多可参加的任务数。

有三种可选方案:

1.每次都选取结束时间最早的工作。

2.每次都选取用时最短的工作。

3.每次都选取与最少可选工作有重叠的工作。

实验证明:第一个是正确的,其他两种都可找到反例。

证明:

显然,该算法最后选出的区间不互相重叠,下面证明所选出区间的数量是最多的。设fi为该算法所接受的第i个区间的右端点坐标,gi为某最优解中的第i个区间的右端点坐标。

命题1.1  当i>=1时,该算法所接受的第i个区间的右端点坐标fi<=某最优解中的第i个区间的右端点坐标gi。

该命题可以运用数学归纳法来证明。对于i=1,命题显然为真,因为算法第一个选择的区间拥有最小右端点坐标。令i>1,假定论断对i-1为真,即fi-1<=gi-1。则最优解的第i个可选区间所组成的集合包含于执行该算法时第i个可选区间所组成的集合;而当算法选择第i个区间时,选的是在可选区间中右端点坐标最小的一个,所以有fi<=gi。证毕。

设该算法选出了k个区间,而最优解选出了m个区间。

命题1.2  最优解选出的区间数量m=该算法选出的区间数量k。

假设m>k,根据命题1.1,有fk<=gk。由于m>k,必然存在某区间,在gk之后开始,故也在fk之后开始。而该算法一定不会在选了第k个区间后停止,还会选择更多的区间,产生矛盾。所以m<=k,又因为m是最优解选出区间个数,所以m=k。

综上所述,算法选出的区间是最优解。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int main()
 8 {
 9     int n;
10     char s[2010];
11     cin >> n;
12     for (int i = 1; i <= n; i++)
13         cin >> s[i];
14     int a = 1, b = n;
15     int ans = 0;
16     while (a <= b) {
17         bool flag = 0;
18         for (int i = 0; a + i <= b; i++)
19         {
20             if (s[a + i] < s[b - i])
21             {
22                 flag = 1;
23                 break;
24             }
25             else if (s[a + i] > s[b - i])
26             {
27                 flag = 0;
28                 break;
29             }
30         }
31         if (flag == 1) {
32             ans++;
33             cout << s[a++];
34             if (ans % 80 == 0)    cout << endl;
35         }
36         else {
37             ans++;
38             cout << s[b--];
39             if (ans % 80 == 0)    cout << endl;
40         }
41     }
42     cout << endl;
43     return 0;
44 }
区间调度

 二、字典序最小问题

给定长度为N的字符串S,要构造一个长度为N的字符串T。有两种操作:

1.从S的头部删除一个字符,加到T的尾部

2.从S的尾部删除一个字符,加到T的尾部

目标是要构造字典序尽可能小的字符串T

方法:

反转字符串S的字符串S1

按照字典序比较S和S1

若S较小 就从头部删除

若S1较小 就从尾部删除

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int main()
 8 {
 9     int n;
10     char s[2010];
11     cin >> n;
12     for (int i = 1; i <= n; i++)
13         cin >> s[i];
14     int a = 1, b = n;
15     int ans = 0;
16     while (a <= b) {
17         bool flag = 0;
18         for (int i = 0; a + i <= b; i++)
19         {
20             if (s[a + i] < s[b - i])
21             {
22                 flag = 1;
23                 break;
24             }
25             else if (s[a + i] > s[b - i])
26             {
27                 flag = 0;
28                 break;
29             }
30         }
31         if (flag == 1) {
32             ans++;
33             cout << s[a++];
34             if (ans % 80 == 0)    cout << endl;
35         }
36         else {
37             ans++;
38             cout << s[b--];
39             if (ans % 80 == 0)    cout << endl;
40         }
41     }
42     cout << endl;
43     return 0;
44 }
字典序最小

三、添加守卫

直线上有N个点。从这N个点中选择若干个,给他们加上标记。对每一个点,距离为R以内的区域内必须有带有标记的点。

在满足这个条件的同时,尽可能少的点添加标记。请问至少要有多少点被加上标记。

方法:

从第一个点开始,找到距离R范围内最右边的点加上标记

再从标记的点开始,找R的范围外的第一个点 作为起始点。

从起始点开始重复。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int main()
 8 {
 9     int a, b;
10     while (cin >> a >> b) {
11         if (a ==-1 && b==-1)    break;
12         int s[1010],ans=0;
13         for (int i = 1; i <= b; i++)
14             cin >> s[i];
15         sort(s + 1, s + b + 1);
16         int i = 1;
17         while (i <= b) {
18             int c = s[i++];
19             while (i <= b && s[i] <= a + c)    i++;
20             int p = s[i - 1];
21             ans++;
22             while (i <= b && s[i] <= p + a)    i++;
23         }
24         cout << ans << endl;
25     }
26     return 0;
27 }
View Code

四、木板切割

把一块长木板切割成N块,每次切割木板时,需要的开销为这块木板的长度。

求出将木板切割完最小的开销。

输入 :

3

8  5  8

输出:

34(8+5+13+8)//另一种是(8+8+16+5)不是最小舍去。

方法:

由于每次切木板都是切成两部分,可以参照二叉树。

木板长度*节点的深度。

对于最优解来说:最短的板和次短的板应是兄弟节点,最短的板也应该是深度最大的叶子节点。

在数组中找最小的和次小的,答案加上这两个数,再把这两个数合并起来的一个大数插入数组中(这两个数已经无用),总数-1.

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 int main()
 8 {
 9     int n;
10     long long ans = 0;//long long 
11     int a[20010];
12     cin >> n;
13     for (int i = 1; i <= n; i++)
14         cin >> a[i];
15     while (n > 1) {
16         int min1 = 1, min2 = 2;
17         if (a[min1] > a[min2])    swap(min1, min2);
18         for (int i = 3; i <= n; i++)
19         {
20             if (a[i] < a[min1]) {
21                 min2 = min1;
22                 min1 = i;
23             }
24             else if (a[i] < a[min2]) {
25                 min2 = i;
26             }
27         }
28         int t = a[min1] + a[min2];
29         ans += t;
30         if (min1 == n)    swap(min1, min2);//若a[n-1]是最小的数,就可以不要。
31         a[min1] = t;
32         a[min2] = a[n];
33         n--;
34     }
35     cout << ans << endl;
36     return 0;
37 }
View Code
No matter how you feel, get up , dress up , show up ,and never give up.
原文地址:https://www.cnblogs.com/Kaike/p/9875831.html