2 条题解
-
0Guest LV 0 MOD
-
2
这道题简化之后难度其实并不大。
首先第一个感觉就是贪心。肯定是每个数尽量保留最高位的那一个。但如果某一位有偶数个数保留,就会变回0,所以反而就不是最优秀。这是有个骗分技巧:关于最后的四个点,我们发现数据是成百上千的,而位数只有31位,几乎一定可以覆盖到,于是用贪心填高位,如果已经被填,就更改第一位(这是奇数,如果是原题不限奇偶就尴尬了)
但还是考虑一下正解,毕竟大数据可以出特殊数据来卡,只是我不会造而已。
这时就把本题目转换为一个费用流就可以了。
但是不会的同学可以用下列算法代替,将本题化最大二分图权匹配。可以上网搜索KM算法(也可以看下面)。
———————————————————————————————————————————————————————————————————
将本题剥开,是一道图论题。很容易想到,因为答案属于int型,所以只对应有31位的二进制。将每个数与它二进制中存在1的位相连,而又要尽量的一一匹配,所以是一个二分图带权匹配。总体思路就是能娶女神就娶女神,女神有男朋友了就抢,女神的前男友悲惨的找另一个最优选择,找不到又抢回来,这个男生就只有悲惨的找次优选择,或者让女神的男友找次优选择。总体思路:贪心+抢!
我们用男孩子找女孩来说明。(十八岁以下请绕行)#include<bits/stdc++.h> const int maxn=1e6; inline const void read(int &a) { a=0; char c=getchar(); while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9') { a=(a<<1)+(a<<3)+c-'0'; c=getchar(); } } inline const void write(int a) { if(a>9)write(a/10); putchar(a%10+'0'); } inline const int max(int a,int b) { if(a>b)return a; else return b; } inline const int min(int a,int b) { if(a<b)return a; else return b; } int n,d=0; int point[maxn],next[maxn],to[maxn],len[maxn];//len指这条边的权值,实际上就是指向的那一位的大小,即时这对男女的互相倾心值。 int exboy[maxn],exgirl[maxn],boy[maxn],girl[maxn],three[maxn];//exboy和exgirl表示男孩女孩对暗恋者的期望值。boy和girl分别指这个男女生现任女男友,three是女生离做小三的距离,也就是要被某个男生作为首选,最少还差多少与他的倾心值。 bool visboy[maxn],visgirl[maxn];//记录是否已经参与本次违背伦理的换男女友活动 const bool found(int p)//判断是否能够为这个男生牵线,并返回是否成功 { visboy[p]=true;//这个男生卷进来男生 int side=point[p]; while(side)//遍历他喜欢的每一个女生 { if(!visgirl[to[side]])//每个女生只被抢一次,否则会死循环(多么可怕啊!) { if(exboy[p]+exgirl[to[side]]==len[side])//假设这个女孩能作为首选 { visgirl[to[side]]=true;//纯洁的女孩也被卷了进来 if(!girl[to[side]]||found(girl[to[side]]))//如果女孩没有男友或者他的男友能另找女孩,就在一起! { girl[to[side]]=p;//女生的男朋友 boy[p]=to[side];//男生的女朋友 return true;//共同甜蜜的宣布牵手成功 } } else//这个女孩不是首选 three[to[side]]=min(three[to[side]],exboy[p]+exgirl[to[side]]-len[side]);//这个女孩暗下决心,还要多少倾心值才能成为首选(小三) } side=next[side]; } return false; } void solve() { for(int i=1;i<=n;i++)//试着给每个男孩子找女朋友(找啊找啊找朋友,找到一个女朋友……) { memset(three,0x7f,sizeof(three)); while(true) { memset(visgirl,false,sizeof(visgirl));//女孩没有被抢过 memset(visboy,false,sizeof(visboy));//男孩没有被想过 if(found(i))break; int minus=three[0];//离做小三还有多少距离 for(int j=1;j<=32;j++)if(!visgirl[j])minus=min(three[j],minus);//保证让最容易做小三的那一个做小三。 if(minus==three[0])break;//如果永远做不了小三(three[0]==INF),就做一辈子的单身狗…… for(int j=1;j<=n;j++)if(visboy[j])exboy[j]-=minus;//所有男孩子因为有情敌而降低期望 for(int j=1;j<=32;j++) if(visgirl[j])exgirl[j]+=minus;//女孩们因为又男孩子抢而得意忘形,增加期望。(这个地方可以发现,因为是男女期望之和,所以除了做小三的那个女孩子,其它的配对均不受影响) } } } inline const void add(int a,int b,int c) { next[++d]=point[a];//大家互相牵挂,互做备胎 to[d]=b; point[a]=d; len[d]=c; exboy[a]=max(exboy[a],len[d]);//最初,男孩子都很天真,以为最爱的女神一定属于自己!(马上就是命运的打击了……) } int main() { memset(exboy,0,sizeof(exboy)); memset(exgirl,0,sizeof(exgirl)); memset(girl,0,sizeof(girl)); memset(boy,0,sizeof(boy)); read(n); int k; for(int i=1;i<=n;i++) { read(k); for(int j=1;j<=32;j++)//三十二位美丽的女嘉宾登场!!!这里其实可以保证更加美丽的女嘉宾后加边。根据链式前向星的特点,后加入的美女一定越靠近每个点的第一条边,更先被考虑。 if((1<<j-1)&k)add(i,j,1<<j-1);//互相倾慕的对象,在其脉脉传情之时牵带权线 } solve(); int ans=0; for(int i=1;i<=n;i++) { if(boy[i])ans+=1<<boy[i]-1;//情侣们炫耀这爱情的成果!!! else ans^=1;//关于狗们:这里是一个贪心,如果这个男孩子是个单身狗,他就只能骚扰最丑的那个女生,我们不让他影响各位女神们。这个悲惨的男孩子也只能做些这种事情了……(如LYC) } write(ans); return 0; }
如果你觉得这篇题解太污,请不要大声喧哗,拍照,以免影响他人。
-
1
贪心一波:因为这道题有个特殊的限制,即边权等于女生的点权。所以直接用一种很神奇的贪心方法,从高到低查找所有女生,每一次寻找所连男生中潜力最大者相连(即接下来还有较大的连边)。但是在真正的带权匹配中,这个算法是错误的。参见另一篇题解。
#include<bits/stdc++.h> using namespace std; int n,a[1001],v[32][1001],h[1001],ans2[32],b[1001][31]; long long ans=0; struct node { int a,data; }; inline const void read(int &a) { a=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') { a=(a<<1)+(a<<3)+c-'0'; c=getchar(); } } bool com1(node aa,node bb) { return aa.data<bb.data; } void find(node *x,int len) { node temp[1001]; int jk=0; sort(x+1,x+1+len,com1); if(x[1].data==0) { h[x[1].a]=1; return; } temp[++jk].a=x[1].a; for(int i=1;i<=b[x[1].a][0];i++) { if(b[x[1].a][i]<x[1].data) { temp[jk].data=b[x[1].a][i]; break; } } for(int i=2;i<=len;i++) { if(x[i].data==x[i-1].data) { temp[++jk].a=x[i].a; for(int o=1;o<=b[x[i].a][0];o++) { if(b[x[i].a][o]<x[i].data) { temp[jk].data=b[x[i].a][o]; break; } } } else break; } if(jk==1) { h[temp[jk].a]=1; return; } else { find(temp,jk); } } bool com(int a,int b) { return a>b; } int main() { //freopen("ji.in.txt","r",stdin); int i,k,s; memset(v,0,sizeof(v)); memset(h,0,sizeof(h)); memset(ans2,0,sizeof(ans2)); read(n); for(i=1;i<=n;i++) read(a[i]); for(i=1;i<=n;i++) { for(k=31;k>=0;k--) { if(((unsigned int)1<<k)<=a[i]&&(a[i]&((unsigned int)1<<k))) { b[i][++b[i][0]]=k; v[k][++v[k][0]]=i; } } } for(i=31;i>=0;i--) { if(v[i][0]) { if(v[i][0]==1&&!h[v[i][1]]) { ans2[i]=1; h[v[i][1]]=1; } else { node temp[1001]; int jk=0; for(int j=1;j<=v[i][0];j++) { if(!h[v[i][j]]) { temp[++jk].a=v[i][j]; int mm=0; for(int o=1;o<=b[v[i][j]][0];o++) { if(!ans2[b[v[i][j]][o]]&&b[v[i][j]][o]<=i) { mm++; if(mm==2) { temp[jk].data=b[v[i][j]][o]; break; } } } } } if(jk) { ans2[i]=1; find(temp,jk); } } } } for(i=1;i<=n;i++) { if(!h[i]) ans2[0]=abs(ans2[0]-1); } for(i=0;i<=31;i++) if(ans2[i]) ans+=((unsigned int)1<<i); cout<<ans<<endl; return 0; }
- 1
信息
- 难度
- 9
- 分类
- (无)
- 标签
- (无)
- 递交数
- 14
- 已通过
- 3
- 通过率
- 21%
- 上传者