我们按三个部分来学:
注意
无后效性:某阶段的状态一旦确定,则此后过程的决策不再受此前各种状态及决策的影响。
有后效性:就是某个状态之后要做的决策会受之前的状态及决策的影响。(摘自“思维的深度”)
如果把条件改为:可以往前后左右走但是不能走重复的格子,那么接下来要做的决策就需要考虑之前的决策,故此时是有后效性。
若仍不懂请看例题
生活中的找零问题: 假如老板要找给我99分钱,他有有的面值分别为25分,10分,5分,1分(现实生活中没有)的硬币数,为 了找给我最少的硬币数,老板应该怎么做?
用贪心解出为:3枚19分,0枚15分和3枚1分。(共计6枚)
而最优解是:4枚15分。(共计6枚)
这就是后效性。那我们继续教学
题目描述
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序, 使得n个人的平均等待时间最小。 n<=1000,等待的时间小于等于1000
输入
输入文件共两行,第一行为n;第二行分别表示第 1 个人到第n个人每人的接水时间T1,T2,…,Tn,每 个数据之间有 1 个空格。
输出
输出有一行,最少平均排列方案下的平均等待时间(输出结果精确到小数点后两位)。
样例输入 Copy
10 56 12 1 99 1000 234 33 55 99 812
样例输出 Copy
532.00
你可以这么想:
每个人都很贪心,都想先接水,打了起来,现在最优方案是接水的时间短的先接,这样后面等待时间较少,而子问题就是这两两比较的过程。(是不是感觉自己行了呢,去吧)
#include<bits/stdc++.h>
using namespace std;
int a[1010];
int main(){
int n;
int cnt=0;
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++) {
cnt+=a[i]*(n-i+1);
}
printf("%.2lf",cnt*1.0/n );
return 0;
}
题目描述
键盘输入一个高精度的正整数n(n<10^240),去掉其中任意s个数字后剩下的数字按照原来的次序将组成一个新的非负整数。编程对给定的n和s,寻求一种方案,使得剩下组成的新数最小。
输入
第1行:一个正整数n;
第2行:s(s<=n的位数).
输出
最后剩下的最小数。
样例输入 Copy
175438 4
样例输出 Copy
13
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
int n;
cin>>s>>n;
while(n--) {
for(int i=0;i<s.size();i++) {
if(i==s.size()-1||s[i]>s[i+1]) {
s.erase(i,1);
break;
}
}
}
while(s[0]=='0'&&s.size()>1) s.erase(0,1);
cout<<s;
return 0;
}
题目描述
学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。
现在给出n个活动使用礼堂的起始时间bi和结束时间ei(bi < ei<=32767),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。
输入
第一行一个整数n(n<=1000); 接下来的n行,每行两个整数,第一个bi,第二个是ei(bi < ei<=32767)
输出
输出最多能安排的活动个数
样例输入 Copy
11 3 5 1 4 12 14 8 12 0 6 8 11 6 10 5 7 3 8 5 9 2 13
样例输出 Copy
4
提示
注:每个活动占用礼堂的时间为左闭右开区间[bi,ei).
#include<bits/stdc++.h>
using namespace std;
struct Activity{
int start,end;
} act[1010];
bool cmp(Activity a, Activity b) {
return a.end<b.end;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>act[i].start>>act[i].end;
}
sort(act, act + n, cmp);
int ans=1,last=act[0].end;
for(int i=1;i<n;i++){
if(act[i].start>=last){
ans++;
last = act[i].end;
}
}
cout<<ans;
return 0;
}
题目描述
给n个区间,形式为[a, b],a和b均为整数,且a < b。求一个最小的整数点的集合,使得每个区间至少 1 个元素属于这个集合。求这个集合的元素个数。
输入
第1行:1个整数n(1 <= n <= 10000)接下来n行,每行2个整数,表示区间的左右端点a, b(0 <=a < b <= 10000)
输出
第1行:1个整数,表示集合的元素的个数
样例输入 Copy
4 3 6 2 4 0 2 4 7
样例输出 Cop
2
#include<bits/stdc++.h>
using namespace std;
struct human{
int l,r;
} a[10010];
bool cmp(human x,human y) {
return x.r<y.r;
}
int main(){
int n;
cin>>n;
int cnt=1;
for(int i=1;i<=n;i++) {
cin>>a[i].l>>a[i].r;
}
sort(a+1,a+n+1,cmp);
int x=a[1].r;
for(int i=2;i<=n;i++) {
if(a[i].l>x) {
cnt++;
x=a[i].r;
}
}
cout<<cnt;
return 0;
}
题目描述
有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸
牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 N=4,4 堆纸牌数分别为:① 9 ② 8 ③ 17 ④ 6 移动3次可达到目的: 从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3
张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。
输入
N(N 堆纸牌,1 <= N <= 100)
A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
输出
所有堆均达到相等时的最少移动次数。
样例输入 Copy
4 9 8 17 6
样例输出 Copy
3
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,p=0,js=0; cin >>a;
int q[a];
for (int y=0;y<a;y++){
cin >>q[y]; p+=q[y];
}
p/=a;
for (int y=0;y<a;y++) q[y]-=p;
for (int y=0;y<a;y++) {
if (q[y]==0) continue;
q[y+1]+=q[y];
js++;
}
cout<<js;
return 0;
}
最大整数
题目描述
设有 n 个正整数(n≤20),将它们联接成一排,组成一个最大的多位整数。 例如:n=3 时,3 个整数 13,312,343 联接成的最大整数为:34331213 又如:n=4 时,4 个整数 7,13,4,246 联接成的最大整数为:7424613
输入
n 个数
输出
联接成的多位数
样例输入 Copy
3 13 312 343
样例输出 Copy
34331213
#include<bits/stdc++.h>
using namespace std;
bool cmp(string x,string y){
return x+y>y+x;
};
string s[100];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++) cout<<s[i];
return 0;
}
零件分组
题目描述
某工厂生产一批棍状零件,每个零件都有一定的长度Li和重量Wi 。现在为了加工需要,要将它们分成若干组,使每一组的零件都能排成一个长度和重量都不下降(若 i<j,则 Li<=Lj,Wi<=Wj)的序列。请问至少要分成几组?
输入
第一行为一个整数 N(N<=1000),表示零件的个数。第二行有N对正整数,每对正整数表示这些零件的长度和重量,长度和重量均不超过 10000。
输出
仅一行,即最少分成的组数。
样例输入 Copy
5 8 4 3 8 2 3 9 7 3 5
样例输出 Copy
2
#include<bits/stdc++.h>
using namespace std;
struct node{
int h,w;
} a[1010];
bool vis[1010];
bool cmp(node x,node y){
if(x.h==y.h) return x.w<y.w;
return x.h<y.h;
};
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].w>>a[i].h;
sort(a+1,a+n+1,cmp);
int g=0;
for(int i=1;i<=n;i++) {
if(vis[i]==0) {
g++;
vis[i]=1;
int fr=a[i].w;
for(int j=i+1;j<=n;j++) {
if(vis[j]==0&&a[j].w>=fr) {
vis[j]=1;
fr=a[j].w;
}
}
}
}
cout<<g;
return 0;
}
纪念品组合
题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入
输入文件包含n+2 行: 第 1 行包括一个整数w ,为每组纪念品价格之和的上限。 第 2 行为一个整数n ,表示购来的纪念品的总件数。 第 3~ n +2 行每行包含一个正整数 p i (5 <= p i <= w ),表示所对应纪念品的价格。
输出
输出文件仅一行,包含一个整数,即最少的分组数目。
样例输入 Copy
100 9 90 20 20 30 50 60 70 80 90
样例输出 Copy
6
提示
50%的数据满足:1 <= n <= 15 100%的数据满足:1 <= n <= 30000, 80 <= w <= 200
#include<bits/stdc++.h>
using namespace std;
int a[10000010];
int main(){
int n,m;
cin>>m>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
sort(a+1,a+n+1);
int l=1,r=n;
int sum=n;
while(l<r) {
if(a[l]+a[r]<=m) {
l++;
r--;
sum--;
}
else if(a[l]+a[r]>m) r--;
}
cout<<sum;
}
乘积最大 2
题目描述
要求将n写成若干个正整数之和,并且使这些正整数的乘积最大。
输入
读入一个正整数n(10≤n≤31000)。
输出
第1行输出一个整数,为最大乘积的位数。 第2行输出最大乘积的前100位,如果不足100位,则按实际位数输出最大乘积。
样例输入 Copy
13
样例输出 Copy
3 108
提示
数据范围及提示
在给定的范围内,最大乘积的位数不超过5000位。
#include<bits/stdc++.h>
using namespace std;
int a[99999];
int th,tw,len=1,n;
void mul(int x) {
for(int i=1;i<=len;i++) {
a[i]=a[i]*x+a[i-1]/10;
a[i-1]%=10;
}
if(a[len]>=10) {
a[len+1]=a[len]/10;
a[len]%=10;
len++;
}
}
int main(){
cin>>n;
while(n>=5) {
n-=3;
th++;
}
a[1]=1;
for(int i=1;i<=th;i++) mul(3);
mul(n);
cout<<len<<endl;
for(int i=len;i>=max(len-99,1);i--) {
cout<<a[i];
}
return 0;
}
独木桥
请跳转至《洛谷经典题目——独木桥》
后面没来得及写分析。
最后祝大家元旦快乐