贪心专题
1.一些贪心问题并没有很强的直观性,但是如果把题目的限制巧妙转化一下达到去除限制依然不影响最优解的情况下,或许此时的贪心策略会更好发现。
2.Dilworth定理:对于一个有限偏序集,链的最少划分数=最长反链长度。反链的最少划分数=最长链长度。
luogu 1007 独木桥
此题的限制在于蚂蚁相遇后会各自调转方向,但如果把蚂蚁相遇调转方向看成直接穿过,显然是不影响答案的。
于是贪心策略就出来了。要求对于一个整体的最短时间或者最长时间,而这个整体中个体之间的运动是不会受到影响的,于是我们显然可以由个体最优解推导出整体最优解。
于是最短时间就是坐标大于中点的往右走,小于中点的往左走。最长时间就是坐标大于中点的往左走,小于中点的往右走。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-9 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=1005; //Code begin... int main () { int l, n, x, ans1=0, ans2=0; scanf("%d%d",&l,&n); FOR(i,1,n) { scanf("%d",&x); if (2*x<l+1) ans1=max(ans1,x), ans2=max(ans2,l+1-x); else ans1=max(ans1,l+1-x), ans2=max(ans2,x); } printf("%d %d\n",ans1,ans2); return 0; }
luogu 1016 旅行家的预算
此题的限制在于油箱的容量是有限的,而我们并不知道之后的哪些站油量会更便宜。到了这时思维受到了阻碍。
如果我们把问题转化一下,看成往油箱里面加油不需要付钱,而用油则需要付钱,这样显然是不会影响最后的答案的。
于是我们每经过一次加油站的时候都可以保证油箱里的油是满的,因为加油不用付钱,但是我们经过加油站时,如果发现现在的油箱里面有些油比这个加油站的油还要贵的话,
那么我们可以把这些油换掉。而我们每次开车使用油的时候,都优先使用油价最便宜的油。
实现过程使用小根堆维护即可。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-7 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=105; //Code begin... struct qnode{ double cost, V; qnode(double _cost=0, double _V=0):cost(_cost),V(_V){} bool operator<(const qnode&r)const{return cost>r.cost;} }; double dis[N], c[N]; priority_queue<qnode>que; int main () { qnode tmp, temp; double D1, D2, C, P, ans=0; int n; scanf("%lf%lf%lf%lf%d",&D1,&C,&D2,&P,&n); que.push(qnode(P,C)); FOR(i,1,n) scanf("%lf%lf",dis+i,c+i); FOR(i,1,n) { double d=(dis[i]-dis[i-1])/D2; bool flag=false; while (!que.empty()) { tmp=que.top(); que.pop(); if (tmp.V>=d) { ans+=d*tmp.cost; tmp.V-=d; flag=true; if (fabs(tmp.V)>eps) que.push(tmp); break; } else ans+=tmp.V*tmp.cost, d-=tmp.V; } if (!flag) {puts("No Solution"); return 0;} d=(dis[i]-dis[i-1])/D2; tmp=qnode(c[i],d); while (!que.empty()) { temp=que.top(); if (tmp.cost<temp.cost) tmp.V+=temp.V, que.pop(); else break; } que.push(tmp); } double d=(D1-dis[n])/D2; bool flag=false; while (!que.empty()) { tmp=que.top(); que.pop(); if (tmp.V>=d) { ans+=d*tmp.cost; tmp.V-=d; flag=true; if (fabs(tmp.V)>eps) que.push(tmp); break; } else ans+=tmp.V*tmp.cost, d-=tmp.V; } if (!flag) {puts("No Solution"); return 0;} printf("%.2f\n",ans); return 0; }
luogu 1020 导弹拦截
第一问LIS,第二问问最少的LIS划分数。
首先可以转化为最小路径覆盖的问题。有经典的二分图匹配的办法。
一个更优的办法是运用Dilworth定理,即最少链划分数=最长反链长度。于是答案就是倒着做一遍LIS。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-7 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=105; //Code begin... int a[N], dp1[N], dp2[N], pos; int main () { int x, ans1=0, ans2=0; while (~scanf("%d",&x)) a[++pos]=x; FOR(i,1,pos) { dp1[i]=dp2[i]=1; for (int j=i-1; j>=1; --j) { if (a[i]<=a[j]) dp1[i]=max(dp1[i],dp1[j]+1); if (a[i]>a[j]) dp2[i]=max(dp2[i],dp2[j]+1); } ans1=max(ans1,dp1[i]); ans2=max(ans2,dp2[i]); } printf("%d\n%d\n",ans1,ans2); return 0; }
BZOJ 1034 泡泡堂
这题跟田忌赛马差不多。先把两边排个序,然后让最小的最小的比较,能赢则赢,否则让最大的和最大的比较,能赢则赢,否则拿最小的和最大的比较。
对于第二问,因为比分之和是一定的,因此对于第二个队的最优策略就是第一个队的最劣策略。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-7 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... int a[N], b[N]; int main () { int n, ans1=0, ans2=0; scanf("%d",&n); FOR(i,1,n) scanf("%d",a+i); FOR(i,1,n) scanf("%d",b+i); sort(a+1,a+n+1); sort(b+1,b+n+1); int al=1, ar=n, bl=1, br=n; while (al<=ar) { if (a[al]>b[bl]) ++al, ++bl, ans1+=2; else if (a[ar]>b[br]) --ar, --br, ans1+=2; else if (a[al]==b[br]) ++al, --br, ans1+=1; else ++al, --br; } al=1, ar=n, bl=1, br=n; while (al<=ar) { if (b[bl]>a[al]) ++bl, ++al, ans2+=2; else if (b[br]>a[ar]) --br, --ar, ans2+=2; else if (b[bl]==a[ar]) ++bl, --ar, ans2+=1; else ++bl, --ar; } ans2=2*n-ans2; printf("%d %d\n",ans1,ans2); return 0; }