洛谷 P4660 bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈
題目:https://www.luogu.org/record/show?rid=12702916
https://www.lydsy.com/JudgeOnline/problem.php?id=1168
一眼不可做...即使數據范圍很小...
#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; } 還寫過10分的錯誤思路首先,設每種顏色左右手套個數為 l[i],r[i],如果有 l[i]=0 或 r[i]=0,顯然最劣情況要選,所以提前處理好;
設一個狀態 ans(L),表示左手套取 L 個時的答案;
顯然,這個答案是唯一的而且最劣的,現在要找到這個答案;
取 L 個左手套,根據取法的不同,可以取出許多種顏色集合,把每一個可能的集合記作 T;
而這些顏色集合又組成了一個集合,記作 A(L),表示選 L 個左手套會產生的顏色集合的集合;
考慮每個集合 T,在右邊都對應一種最劣的選法,就是把不在 T 中的顏色都選了,最后再+1;
設這個為 R(T),即 R(T) = (∑r[i]) + 1,其中 i 不在集合 T 中;
所以,ans(L) = max{ R(T) },其中 T 是 A(L) 中的一個集合,這樣做很妙地沒有限制什么而找到了最劣答案,保證了正確性;
現在,考慮 A(L) 中有哪些 T,發現:
A(L) = { T | |T| <= L <= S(T) },其中 |T| 是 T 這個集合包含的顏色數量,S(T) 是 T 這個集合包含的那些顏色的手套個數的和;
由于我們找的是 max{ R(T) },而 |T| 越小,R(T) 越大,所以最終 ans(L) 的取值會是合法范圍內某個 |T| 最小的,所以可以暫時忽略 |T| <= L 這個條件!!
所以,現在就變成若 S(T) >= L,則 R(T) 可以更新 ans(L);
然后,可以發現,會成為最終答案的 L,應該是某些顏色的手套全選,最后再+1,否則...應該不是最優的(感性理解...);
所以處理出這種 L,找 S(T) >= L 中 R(T) 最大的,可以用單調棧實現;
用結構體存一種集合的 S(T) 和 R(T),S(T) 同時也是上面所說的 L,按 S(T) 從小到大排序,不斷彈棧令棧內 R(T) 是單調遞減的,就能找到一個結構體后面(S(T) >= L)的 R(T) 最大值,對于一個 R(T) 也能去更新前面同等條件最小的 L;
還有些疑惑之處...就是一些 L 相同的結構體,排序時按 r<y.r 或 r>y.r 都可以過...但是感覺應該是 r<y.r 才對!后面就可以把前面都彈掉,則棧里后面元素的 S(T) 一定 >= L+1;
但如果棧里最后只留下一個值...難道因為這樣構造所以不會出現這種情況?
總之勉強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; }?
轉載于:https://www.cnblogs.com/Zinn/p/9880913.html
總結
以上是生活随笔為你收集整理的洛谷 P4660 bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flask wtforms
- 下一篇: JWT认证不通过导致不能访问视图的解决方