区间问题:
区间选点问题 右端点排序,now标记点。
数轴上有N个闭区间[Ai, Bi]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。
输入
第1行:一个整数N(1 <= N <=100000)
接下来N行,每行2个整数Ai,Bi(-10^7 <= Ai < Bi < 10^7)
输出
第1行:一个整数,表示满足条件的最少点数。
#include <bits/stdc++.h> using namespace std; const int N =100005; #define ri register int struct dian{ int l,r; bool operator <(const dian &t)const { if(r==t.r) return l>t.l; return r<t.r; } }p[N]; int n,m,cent; int main(){ scanf("%d",&n); for(ri i=1;i<=n;i++) scanf("%d%d",&p[i].l,&p[i].r); sort(p+1,p+1+n); for(ri i=1;i<=n;i++) cent=1; int now=p[1].r; for(ri i=2;i<=n;i++) { if(p[i].l>now) cent++,now=p[i].r; } printf("%d ",cent); }
区间覆盖问题
数轴上有N个闭区间[Ai, Bi],选择尽量少的区间覆盖一条指定线段[S, T]。
题解:(1)找到所有的、起点小于s的区间
(2)把这些区间按照起点,从小到大排序
(3)选择终点最大的那个区间,设这个最大的终点是bi
(4)现在问题变成了,“选择尽量少的区间覆盖一条指定线段[bi, t]
输入
第1行:先是一个整数N(1 <= N <=10^6),然后是两个整数S和T(-10^7 <= S < T <= 10^7)
接下来N行,每行2个整数Ai,Bi(-10^7 <= Ai < Bi < 10^7)
输出
第1行:一个整数,表示最少需要的区间的个数。如果无解,输出 No Solution
#include <bits/stdc++.h> using namespace std; const int N =10005; const int M =100005; #define ri register int struct dian{ int l,r; bool operator <(const dian &t)const { if(l==t.l) return r<t.r; return l<t.l; } }p[N]; int n,s,e; int main(){ scanf("%d%d%d",&n,&s,&e); int now=s;int cent=0; for(ri i=1;i<=n;i++) { int a,b; scanf("%d%d",&a,&b); if(a>e||b<s) continue; p[++cent].l=a,p[cent].r=b; } sort(p+1,p+1+cent); int i=1,num=0; while(now<e) { int k=now; for(;p[i].l<=k&&i<=cent;i++) { now=max(now,p[i].r); } num++; if(now==k) {printf("No Solution");return 0;} } printf("%d",num); }
区间划分集合问题
题目 时间轴上有n个开区间(ai, bi),把这些区间至少划分成多少个集合,使得每个集合中的区间两两没有公共点。因为是开区间,所以(1, 2)和(2,3)可在一个集合中。
题 解 先将所有区间按左端点排序排序。 用一个数组来存当前每个已知集合最右的端点。若没有任何集合左端点比此区间小,就增加一个集合。 若有,就取可行集合中最大 的右端点所在集合,并更新。
输入
第1行:一个整数N(1 <= N <=10^5)
接下来N行,每行2个整数Ai,Bi(0<= Ai < Bi < 10^7)
输出
第1行:1个整数,需要划分成的最少集合数。
#include <bits/stdc++.h> using namespace std; const int M =100005; const int N =100005; #define ri register int struct dian{ int l,r; bool operator <(const dian &t)const { if(l==t.l) return r>t.r; return l<t.l; } }p[N]; int n,m,f[N],k[N]; int main(){ scanf("%d",&n); for(ri i=1;i<=n;i++) scanf("%d%d",&p[i].l,&p[i].r); sort(p+1,p+1+n); k[1]=p[1].r; int cent=1; for(ri i=2;i<=n;i++) { int flag=0; for(ri j=1;j<=cent;j++) { if(p[i].l>k[j]) flag=1,k[j]=p[i].r; } if(!flag) k[++cent]=p[i].r; } printf("%d",cent); }
总结 : 区间问题先排序,在具体分析,(排序后少了个限制条件)
流水线问题:
题解 :
(1)把在A车间加工时间最短的部件最先加工,这样使得B车间能更快开始加工。
(2)把在B车间加工时间最短的部件最后加工,这样使得A车间的空闲时间最短。(反正B一定要A先加工,A多加工出来的继续加工下一个就行了)
题目描述
某工厂收到了n个产品的
订单,这n个产品分别在A、B两个车间加工,并且必须先在A车间加工后才可以到B车间加工。
某个产品i在A、B两车间加工的时间分别为Ai、Bi。怎样安排这n个产品的加工顺序,才能使总的加工时间最短。这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在A、B两车间加工完毕的时间。
输入格式
第一行仅—个数据n(0<n<1000),表示产品的数量。
接下来n个数据是表示这n个产品在A车间加工各自所要的时间(都是整数)。
最后的n个数据是表示这n个产品在B车间加工各自所要的时间(都是整数)。
输出格式
第一行一个数据,表示最少的加工时间;
第二行是一种最小加工时间的加工顺序。
输入输出样例
5 3 5 8 7 10 6 2 1 4 9
34 1 5 4 2 3
#include <bits/stdc++.h> using namespace std; const int M =26000; #define ri register int int a[M],b[M],c[M]; long long t[M]; struct dian{ int id,cost; }p[M]; bool cmp(dian a,dian b) { return a.cost<b.cost; } int n,ans[M]; int main(){ scanf("%d",&n); for(ri i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]) ,p[i].cost=min(a[i],b[i]),p[i].id=i; sort(p+1,p+1+n,cmp); int s=0,e=n+1; for(ri i=1;i<=n;i++) { if(p[i].cost==a[p[i].id]) ans[++s]=p[i].id; else ans[--e]=p[i].id; } for(ri i=1;i<=n;i++) t[i]=t[i-1]+a[ans[i]]; long long sum=t[1]+b[ans[1]]; for(ri i=2;i<=n;i++) sum=max(sum,t[i])+b[ans[i]]; printf("%lld",sum); }
矩阵问题:
进行压缩,多行压缩成一行(多维转一唯)
题目:
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,求最大非空子矩阵。 例如: 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 最大子矩阵是: 9 2 -4 1 -1 8 输入 两行: 第一行n 然后n行,每行n个数, 代表一个n*n的矩阵。 输出 最大子矩阵 样例输入 Copy 4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 样例输出 Copy 15
代码:
#include <bits/stdc++.h> using namespace std; const int M =1005; #define ri register int int arr[M][M],b[M]; int maxla(int a[],int n) { int sum=a[1],ans=a[1]; for(ri i=2;i<=n;i++) { if(sum+a[i]>a[i]) sum+=a[i]; else sum=a[i]; if(sum>ans) ans=sum; } return ans; } int n; int main(){ scanf("%d",&n); int ans=-45454564; for(ri i=1;i<=n;i++) for(ri j=1;j<=n;j++) scanf("%d",&arr[i][j]); for(ri i=1;i<=n;i++) { for(ri j=1;j<=n;j++) b[j]=0; for(ri j=i;j<=n;j++) { for(ri k=1;k<=n;k++) { b[k]+=arr[j][k]; } int sum=maxla(b,n); ans=max(sum,ans); } } printf("%d",ans); }
单位 罚款 问题
方法 和之前 一个 类似 优先队列 搞一搞
题目
问题 O: 不守交规 时间限制: 1 Sec 内存限制: 64 MB 题目描述 近些年来,生活水平越来越好,私家车也成了很多家庭必备之物。但某些司机总是不守交规,罚单也是接踵而至。 有一位不遵守交规的司机,在同一天收到了n条违章罚单短信(1≤n≤100),每条罚单短信中有两个内容,一:交罚款的最后剩余时间ti;二:过期未交的滞纳金mi(1≤ti,mi≤1000),假设不管过期多少天,滞纳金数量不会改变,而且,这位司机很忙,每天最多只能处理一张罚单,那么,这位司机应该按怎样的处理违章短信的顺序,才能使滞纳金总和最少? 输入 共n+1行 第1行:收到短信数n 后n行:每行分别两个数,最后期限ti和过期滞纳金mi,用空格隔开 输出 最少的滞纳金总和 样例输入 Copy 4 1 50 1 100 2 60 3 60 样例输出 Copy 50
代码:
// 14 36 #include <bits/stdc++.h> using namespace std; const int M =1005; #define ri register int struct dian{ int cost,ed; bool operator <(const dian &k)const { return ed<k.ed; } }p[M]; int n; struct cmp{ bool operator ()( int &a, int &b) { return a>b; } }; priority_queue <int,vector<int>,cmp> q; int main(){ scanf("%d",&n); for(ri i=1;i<=n;i++) scanf("%d",&p[i].ed); for(ri i=1;i<=n;i++) scanf("%d",&p[i].cost); sort(p+1,p+1+n); int trmp=0,ans=0; for(ri i=1;i<=n;i++) { if(trmp+1<=p[i].ed) q.push(p[i].cost),trmp++; else if(trmp<=p[i].ed&&q.top()<p[i].cost) ans+=q.top(),q.pop(),q.push(p[i].cost); else ans+=p[i].cost; } cout<<ans; }
反向思考:
题目
问题 P: 【USACO TRAINING】修理牛棚 时间限制: 1 Sec 内存限制: 64 MB 题目描述 在一个暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 剩下的牛一个紧挨着另一个被排成一行来过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 在门被吹飞以后,农民约翰必须尽快在牛棚之前竖立起新的木板。 他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板。 农民约翰想将他购买的木板总长度减到最少。 给出可能买到的木板最大的数目 M(1<= M<=50),牛棚的总数S(1<= S<=200),牛棚里牛的数目C(1 <= C <=S) 和牛所在的牛棚的编号(1 <= stall_number <= S),计算拦住所有有牛的牛棚所需木板的最小总长度。 输出所需木板的最小总长度作为的答案。 输入 第1行:3个整数 M,S和C 第2..C+1行:每行一个整数,表示牛所占的牛棚的编号。 输出 第1行:一个整数,表示所需木板的最小总长度。 样例输入 Copy 4 50 18 3 4 6 8 14 15 16 17 21 25 26 27 30 31 40 41 42 43 样例输出 Copy 25
代码:
#include <bits/stdc++.h> using namespace std; const int M =1005; #define ri register int int a[M],b[M]; int n,x,c; int cmp(int a,int b) { return a>b; } int main(){ scanf("%d%d%d",&n,&x,&c); if(n>=c) { printf("%d",c);return 0;} for(ri i=1;i<=c;i++) scanf("%d",&a[i]); sort(a+1,a+1+c); int ans=a[c]-a[1]+1; for(ri i=2;i<=c;i++) b[i-1]=a[i]-a[i-1]; sort(b+1,b+c,cmp); for(ri i=1;i<n;i++) { ans-=b[i]; } printf("%d",ans+n-1); }
1
数学类型的贪心: 结合公式? 前后比较?
题目
题意: 现在有n个人,有两个问题x,y 每个人对应有一个xi yi 表示解决这道题的分数,当然这里也有一些关系,u v 表示u v 不能组成一组做题,那么问你每个人和其他所有能组队的人做这两道题的最小分数是多少。也就是问你对于当前的 xi yi 所有能组队的j min(xi+yj,yi+xj) 的最小值的和。
代码
#include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int M =600005; const int N =300005; typedef long long ll; #define ri register int int n,m; ll x[N],y[N],ans[N],a[N],sum; bool cmp(int aa,int bb) { if(x[aa]-y[aa]<x[bb]-y[bb]) return 1; return 0; } int head[N],cent; struct setbian{ int net,to; }bian[M]; void add(int a,int b) { bian[++cent].net=head[a],head[a]=cent; bian[cent].to=b; } int main(){ scanf("%d%d",&n,&m); for(ri i=1;i<=n;i++) { scanf("%lld%lld",&x[i],&y[i]); a[i]=i; } for(ri i=1;i<=m;i++) { int aa,bb; scanf("%d%d",&aa,&bb); add(aa,bb);add(bb,aa); } sort(a+1,a+1+n,cmp); for(ri i=1;i<=n;i++) { int id=a[i]; ans[id]+=sum+(i-1)*y[id]; sum+=x[id]; } sum=0; for(ri i=n;i>=1;i--) { int id=a[i]; ans[id]+=sum+(n-i)*x[id]; sum+=y[id]; } for(ri i=1;i<=n;i++) for(ri j=head[i];j;j=bian[j].net) { int v=bian[j].to; ans[i]-=min(x[i]+y[v],y[i]+x[v]); } for(ri i=1;i<=n;i++) printf("%lld ",ans[i]); }
的