【算法】贪心
1>不相交区间区间问题
活动安排
求不相交区间个数
这里选择将r作为限制,以达到全局最优的目的
策略:选择对后面影响小的
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; int n; const int N=1003; struct node { int l,r; bool operator < (const node & o) { return r==o.r?l<o.l:r<o.r; } }d[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&d[i].l ,&d[i].r ); sort(d+1,d+n+1); int cnt=1,nw=d[1].r ; for(int i=2;i<=n;i++) if(d[i].l >=nw) cnt++,nw=d[i].r ; printf("%d\n",cnt); return 0; }
2>区间选点问题
尽量让每个点给多个区间使用,即尽量在重叠区域种树
因为点的分布可能是:
l 0 0 0 1 1 0 1 1 0 r
所以只能把每个点的数记在每个点上,然后每个点只能种一次,所以用us记录
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; int m,n; const int N=5003; struct node { int l,r,nd; bool operator < (const node & o) { return r==o.r?l<o.l:r<o.r; } }d[N]; int tr[30003]; bool us[30003]; int lowbit(int x) { return x&(-x); } void add(int x) { for(;x<=m;x+=lowbit(x)) tr[x]++; } int find(int l,int r) { int ans=0; for(;r;r-=lowbit(r)) ans+=tr[r]; for(;l;l-=lowbit(l)) ans-=tr[l]; return ans; } int main() { scanf("%d%d",&m,&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&d[i].l ,&d[i].r ,&d[i].nd ); sort(d+1,d+n+1); int cnt=0; for(int i=1;i<=n;i++) { int t=d[i].nd - find(d[i].l -1,d[i].r ); if(t>0) { cnt+=t; while(t--) { while(us[d[i].r ]) d[i].r --; add(d[i].r ),us[d[i].r ]=true; } } } printf("%d\n",cnt); return 0; }
3>区间覆盖问题
思路:从左选到右,每次先选可能(即d[i].l<=tot_r), 再选最优(max(d[i].r),然后选了,
为了减少选择时的遍历此时,我们将l严格排序,这样可以尽快找出可行解,
而且当没有可以修改的量时,直接退出,表示无解
#include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<cstring> using namespace std; int t,n,l,w; double ww; const int N=15003; struct node { double l,r; bool operator < (const node & o) const { return l<o.l; } }d[N]; int main() { scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&l,&w); ww=w*1.0/2,ww*=ww; double x,r; for(int i=1;i<=n;i++) { scanf("%lf%lf",&x,&r); if(r*2<=w) d[i].l =d[i].r =0; else { double t=sqrt(r*r-ww); d[i].l =x-t,d[i].r =x+t; } } sort(d+1,d+n+1); // for(int i=1;i<=n;i++) // printf(" %lf %lf\n",d[i].l ,d[i].r ); int nw=1,cnt=0; double tot_r=0; while(nw<=n && tot_r<l) { double tt=tot_r; for(;d[nw].l<=tot_r && nw<=n;nw++) if(d[nw].r >=tt) tt=d[nw].r ; if(tt>tot_r) tot_r=tt,cnt++; else break; } if(tot_r<l) printf("-1\n"); else printf("%d\n",cnt); } return 0; }
4>[经典]加工生产调度
先附上代码,思路即证明再说
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; int n; const int N=1003; struct node { int a,b,c,xh; bool operator < (const node & o) const { return c<o.c; } }d[N]; int ans[N],ta,tb; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&d[i].a ),d[i].xh =i; for(int i=1;i<=n;i++) scanf("%d",&d[i].b ),d[i].c =min(d[i].a ,d[i].b ); sort(d+1,d+n+1); int l=1,r=n; for(int i=1;i<=n;i++) { if(d[i].c ==d[i].a ) ans[l++]=i ; else ans[r--]=i ; } int rest=d[ans[1]].a ; tb=rest; for(int i=1;i<=n;i++) { int u=ans[i]; tb+=max(0,d[u].a -rest)+d[u].b ; rest=d[u].b +max(rest-d[u].a ,0); ans[i]=d[ans[i]].xh ; } printf("%d\n%d",tb,ans[1]); for(int i=2;i<=n;i++) printf(" %d",ans[i]); return 0; }
5>[均分纸牌]糖果传递
看起来很难的样子
实际上是道数学...不想证明了
#include<cstdio> #include<cstdlib> #include<algorithm> #define int long long using namespace std; int n; const int N=1e6+3; int d[N],ave,ans; signed main() { scanf("%lld",&n); for(int i=1;i<=n;i++) scanf("%lld",&d[i]),d[i]+=d[i-1]; ave=d[n]/n; for(int i=1;i<n;i++) d[i]-=ave*i; sort(d+1,d+n); int pos=d[n/2]; ans=max(pos,-pos); for(int i=1;i<n;i++) ans+=abs(d[i]-pos); printf("%lld\n",ans); return 0; }
补充一道极其像贪心的题,但是其实是有后效性的...
石子合并
以警示自己使用贪心前,能证明就证明