Problem B. Milk Tea Google Kickstart Round E 2018
題意:給定N個二進位制數,求出一個二進位制數使得其和N個二進位制數的complaint之和最小。complaint=不相同的bit的個數。另外有M個constraint,求出的二進位制數不能出現在constraint中。
如果沒有constraint,最優解opt貪心即可求出來。假設sum[p]是N個二進位制數bit p的1的個數之和,那麼opt[p]=max(sum[p],N-sum[p])。
入手點是M<100,開始認為是從opt貪心,列舉改變一個bit,改變兩個bit,...直到不出現在constraint中。如果改變一個bit,直接貪心找到complaint最大的一個bit取反即可。但是如果改變兩個bit,只能用搜索了,因為給定一個數組x1,x2,x3,x4,x5,(e.g., 5, 4, 3, 2, 1) 如果從x1開始列舉,可能滿足constraint的組合裡x1+x5滿足條件,但是有可能x2+x3 complaint更大。所以感覺還是要列舉2^p個組合。。。
也想過按照bit DP,但是當時覺得前p個bit滿足constraint這個不好弄,因為constraint是個global的東西。。真是行百里者半九十。
正解是按照bit構造。因為只有M個constraint,所以只要找到complaint最小的前M+1個二進位制數,一定有一個是符合條件的。假設s[0,...,p+1]是前M+1個,那麼s[0,..,p]一定也是前M+1個。Proof by contradiction: 如果s[0,...,p+1]是前M+1個而s[0,...,p]不是,假設s[p+1]增加的complaint是x,那麼排在s[0,...,p]之前的s'[0,...,p]增加一個相同的bit (s[p+1]),構成的新的二進位制數的complaint一定比s[0,...,p+1]小,如此可以產生多餘M+1個complaint比s[0,..,p+1]小的二進位制數,和s[p+1]是前M+1個矛盾。
Then 如果s[0,...,p]不是前M+1個,s[0,..,p+1]一定也不是前M+1個,無論新增的Bit是0 or 1.所以對於每個bit p,維護前M個s[0,..,p],對下一個bit增加0 or 1,再對bit p+1排序找出前M+1個……
可以預處理求出N個二進位制數bit p的1的個數之和,這樣從bit p to bit p+1可以O(1)求出新增的complaint。
#include<iostream> #include<stdio.h> #include<cstdio> #include<string> #include<cmath> #include<stdlib.h> #include<algorithm> #include<string.h> #include<cstring> #include<vector> #include<queue> #include<map> #include<set> #include<ctime> using namespace std; const int maxn=5010; int T; int N; int K; int A[maxn]; int ans; int vis[maxn]; int main() { // freopen("input.txt","r",stdin); freopen("A-large.in","r",stdin); freopen("A.txt","w",stdout); clock_t START_TIME; clock_t FINISH_TIME; START_TIME=clock(); scanf("%d",&T); for(int ca=1;ca<=T;ca++) { memset(A,0,sizeof(A)); memset(vis,0,sizeof(vis)); scanf("%d %d",&N,&K); ans=0; for(int i=0;i<N;i++) { scanf("%d",&A[i]); } sort(A,A+N); int idx=0; for(int i=0;i<N;i++) { int cnt=0; while(cnt<K) { if(vis[idx]==1) { idx++; } else if(A[idx]-i-1<0) { idx++; } else { cnt++; vis[idx++]=1; ans++; } if(idx>=N) { break; } } if(idx>=N) { break; } } printf("Case #%d: %d\n",ca,ans); cerr<<"finish case "<<ca<<endl; } FINISH_TIME=clock(); cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl; return 0; }
附暴力code。。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
const int maxn=110;
int T;
int N;
int M;
int P;
int mp[maxn][maxn];
int sp[maxn][maxn];
int opt[maxn];
int colsum[maxn];
int arr[maxn];
int complaint[maxn];
int tmparr[maxn];
int ans;
bool cmp(int a,int b)
{
return complaint[a]>complaint[b];
}
bool check()
{
for(int i=0;i<M;i++)
{
bool flg2=true;
for(int j=0;j<P;j++)
{
if(opt[j]!=sp[i][j])
{
flg2=false;
break;
}
}
if(flg2==true)
{
return true;//exist the same one
}
}
return false;
}
bool checktmp()
{
for(int i=0;i<M;i++)
{
bool flg2=true;
for(int j=0;j<P;j++)
{
if(tmparr[j]!=sp[i][j])
{
flg2=false;
break;
}
}
if(flg2==true)
{
return true;//exist the same one
}
}
return false;
}
void dfs(int n)
{
if(n==P)
{
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
// return;
if(checktmp()==true)
{
// cout<<"existing ";
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
return;
}
else
{
int tmp=0;
for(int j=0;j<P;j++)
{
if(tmparr[j]!=opt[j])
{
tmp+=max(colsum[j],N-colsum[j]);
}
else
{
tmp+=min(colsum[j],N-colsum[j]);
}
}
// for(int i=0;i<P;i++)
// {
// cout<<tmparr[i]<<" ";
// }
// cout<<endl;
// cout<<tmp<<endl;
ans=min(ans,tmp);
}
return;
}
tmparr[n]=0;
dfs(n+1);
tmparr[n]=1;
dfs(n+1);
tmparr[n]=0;
return;
}
int main()
{
// freopen("input.txt","r",stdin);
freopen("B-small-attempt0.in","r",stdin);
freopen("B-small.txt","w",stdout);
clock_t START_TIME;
clock_t FINISH_TIME;
START_TIME=clock();
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
memset(mp,0,sizeof(mp));
memset(sp,0,sizeof(sp));
memset(opt,0,sizeof(opt));
memset(colsum,0,sizeof(colsum));
memset(arr,0,sizeof(arr));
memset(complaint,0,sizeof(complaint));
memset(tmparr,0,sizeof(tmparr));
ans=0x3f3f3f3f;
scanf("%d %d %d",&N,&M,&P);
for(int i=0;i<N;i++)
{
char tmp[maxn];
scanf("%s",&tmp);
for(int j=0;j<P;j++)
{
mp[i][j]=tmp[j]-'0';
// scanf("%d",&);
// cout<<mp[i][j]<<" ";
}
// cout<<endl;
}
for(int i=0;i<M;i++)
{
char tmp[maxn];
scanf("%s",&tmp);
for(int j=0;j<P;j++)
{
sp[i][j]=tmp[j]-'0';
// scanf("%d",&sp[i][j]);
// cout<<sp[i][j]<<" ";
}
// cout<<endl;
}
for(int j=0;j<P;j++)
{
for(int i=0;i<N;i++)
{
colsum[j]+=mp[i][j];
}
if(colsum[j]*2<=N)
{
opt[j]=0;
}
else
{
opt[j]=1;
}
// arr[j]=j;
// complaint[j]=min(colsum[j],N-colsum[j]);
}
// for(int j=0;j<P;j++)
// {
// cout<<colsum[j]<<" ";
// }
// cout<<endl;
// for(int j=0;j<P;j++)
// {
// cout<<opt[j]<<" ";
// }
// cout<<endl;
// cout<<"dfs"<<endl;
dfs(0);
// sort(arr,arr+P,cmp);
// for(int i=0;i<P;i++)
// {
// if(ckeck()==true)
// {
//
// }
// }
printf("Case #%d: %d\n",ca,ans);
cerr<<"finish case "<<ca<<endl;
}
FINISH_TIME=clock();
cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl;
return 0;
}