洛谷 P4660 bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈
題目:https://www.luogu.org/record/show?rid=12702916
https://www.lydsy.com/JudgeOnline/problem.php?id=1168
一眼不可做...即使數(shù)據(jù)范圍很小...
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=25; int n; ll a[xn],b[xn],inf=1e17; bool cmp(int x,int y){return b[x]>b[y];} int main() {scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);for(int i=1;i<=n;i++)scanf("%lld",&b[i]);ll ansa=0,ansb=0; int tot=0;for(int i=1;i<=n;i++){if(a[i]&&!b[i])ansa+=a[i];else if(!a[i]&&b[i])ansb+=b[i];else if(a[i]&&b[i])a[++tot]=a[i],b[tot]=b[i];}int mx=(1<<tot); ll pa=inf,pb=inf;for(int s=0;s<mx;s++){ll tmpa=0,tmpb=0,mxa=inf,mxb=inf; int cnta=0;for(int i=1;i<=tot;i++){if(s&(1<<(i-1)))tmpa+=a[i],mxa=min(mxa,a[i]-1),cnta++;else tmpb+=b[i],mxb=min(mxb,b[i]-1);}if(cnta==tot)tmpa-=mxa; if(cnta==0)tmpb-=mxb;if(!cnta)tmpa++; else tmpb++; // if(cnta==tot)tmpa-=mxa,tmpb++; // else if(cnta==0)tmpb-=mxb,tmpa++; // else tmpa-=mxa,tmpb++;if(tmpa+tmpb<pa+pb)pa=tmpa,pb=tmpb;}printf("%lld\n%lld\n",pa+ansa,pb+ansb);return 0; } 還寫過(guò)10分的錯(cuò)誤思路首先,設(shè)每種顏色左右手套個(gè)數(shù)為 l[i],r[i],如果有 l[i]=0 或 r[i]=0,顯然最劣情況要選,所以提前處理好;
設(shè)一個(gè)狀態(tài) ans(L),表示左手套取 L 個(gè)時(shí)的答案;
顯然,這個(gè)答案是唯一的而且最劣的,現(xiàn)在要找到這個(gè)答案;
取 L 個(gè)左手套,根據(jù)取法的不同,可以取出許多種顏色集合,把每一個(gè)可能的集合記作 T;
而這些顏色集合又組成了一個(gè)集合,記作 A(L),表示選 L 個(gè)左手套會(huì)產(chǎn)生的顏色集合的集合;
考慮每個(gè)集合 T,在右邊都對(duì)應(yīng)一種最劣的選法,就是把不在 T 中的顏色都選了,最后再+1;
設(shè)這個(gè)為 R(T),即 R(T) = (∑r[i]) + 1,其中 i 不在集合 T 中;
所以,ans(L) = max{ R(T) },其中 T 是 A(L) 中的一個(gè)集合,這樣做很妙地沒有限制什么而找到了最劣答案,保證了正確性;
現(xiàn)在,考慮 A(L) 中有哪些 T,發(fā)現(xiàn):
A(L) = { T | |T| <= L <= S(T) },其中 |T| 是 T 這個(gè)集合包含的顏色數(shù)量,S(T) 是 T 這個(gè)集合包含的那些顏色的手套個(gè)數(shù)的和;
由于我們找的是 max{ R(T) },而 |T| 越小,R(T) 越大,所以最終 ans(L) 的取值會(huì)是合法范圍內(nèi)某個(gè) |T| 最小的,所以可以暫時(shí)忽略 |T| <= L 這個(gè)條件!!
所以,現(xiàn)在就變成若 S(T) >= L,則 R(T) 可以更新 ans(L);
然后,可以發(fā)現(xiàn),會(huì)成為最終答案的 L,應(yīng)該是某些顏色的手套全選,最后再+1,否則...應(yīng)該不是最優(yōu)的(感性理解...);
所以處理出這種 L,找 S(T) >= L 中 R(T) 最大的,可以用單調(diào)棧實(shí)現(xiàn);
用結(jié)構(gòu)體存一種集合的 S(T) 和 R(T),S(T) 同時(shí)也是上面所說(shuō)的 L,按 S(T) 從小到大排序,不斷彈棧令棧內(nèi) R(T) 是單調(diào)遞減的,就能找到一個(gè)結(jié)構(gòu)體后面(S(T) >= L)的 R(T) 最大值,對(duì)于一個(gè) R(T) 也能去更新前面同等條件最小的 L;
還有些疑惑之處...就是一些 L 相同的結(jié)構(gòu)體,排序時(shí)按 r<y.r 或 r>y.r 都可以過(guò)...但是感覺應(yīng)該是 r<y.r 才對(duì)!后面就可以把前面都彈掉,則棧里后面元素的 S(T) 一定 >= L+1;
但如果棧里最后只留下一個(gè)值...難道因?yàn)檫@樣構(gòu)造所以不會(huì)出現(xiàn)這種情況?
總之勉強(qiáng)A了,思路還是很妙的。
代碼如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=25; int n,a[xn],b[xn],ansl,ansr,sta[1<<20],top; struct N{int l,r;bool operator < (const N &y) const{return l==y.l?r<y.r:l<y.l;}// }f[1<<20]; int rd() {int ret=0,f=1; char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();return f?ret:-ret; } void update(int x,int y) {if(ansl+ansr>x+y||(ansl+ansr==x+y&&ansl>x))ansl=x,ansr=y; } int main() {n=rd(); int pl=0,pr=0,tot=0;for(int i=1;i<=n;i++)a[i]=rd();for(int i=1;i<=n;i++)b[i]=rd();for(int i=1;i<=n;i++){if(a[i]&&!b[i])pl+=a[i];else if(!a[i]&&b[i])pr+=b[i];else if(a[i]&&b[i])a[++tot]=a[i],b[tot]=b[i];}int mx=(1<<tot);for(int i=0;i<mx;i++)for(int j=1;j<=tot;j++){if(i&(1<<(j-1)))f[i].l+=a[j];else f[i].r+=b[j];}sort(f,f+mx);for(int i=0;i<mx;i++){while(top&&f[sta[top]].r<f[i].r)top--;sta[++top]=i;}ansl=1e9; ansr=1e9;for(int i=1;i<top;i++)update(f[sta[i]].l+1,f[sta[i+1]].r+1);printf("%d\n%d\n",ansl+pl,ansr+pr);return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/Zinn/p/9880913.html
總結(jié)
以上是生活随笔為你收集整理的洛谷 P4660 bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Flask wtforms
- 下一篇: JWT认证不通过导致不能访问视图的解决方