Codeforces Round #243 425B 425D

来源:互联网 发布:php九九乘法表加表格 编辑:程序博客网 时间:2024/06/06 02:34

十分有趣的两道题目,425D据说是非常经典的题目

425B:

给出一个n*m的01矩阵(100*100),和一个常数k(k<=10),
要求改变尽量少的元素,使得每个01块的的极大填充块都是矩形,
例如,
0 1 0
0 1 0
1 1 1
0 1 0
0 1 0
不满足这个条件,因为1的极大填充块不是矩形,而
0 1 0
0 1 0
1 0 1
0 1 0
0 1 0
满足这个条件。
如果要求改变的最少元素大于k,那么输出-1,否则输出这个答案


思路1:
如果满足最终条件,对于每个一个元素a[i][j],
[a[i][j]   a[i][j+1]  ]
[a[i+1][j] a[i+1][j+1]]
这四个元素有0,2,4个0。
并且,k最大为10,根据这个性质,只需要找出所有这样的不满足的元素,
然后进行深搜即可。

/** * @date    2014-05-07 16:21:44 * @author  jasison * @email   jasison27@gmail.com * @website http://www.jiangshan27.com */#include <cstdio>#include <algorithm>#include <vector>#include <set>using namespace std;typedef pair<int,int> pii;const int N = 105;int n,m,k,ans;int matrix[N][N];set<pii>fault;bool check(int x,int y){if(x>=n-1 || y>=m-1 || x<0 || y<0) {return true;}int cnt=0;for(int i=0;i<2;++i){for(int j=0;j<2;++j){cnt+=(matrix[x+i][y+j]==1);}}return !(cnt&1);}void calc(int d){if(fault.size()==0){ans=min(ans,d);return;}if(d>=k){return;}pii p=*fault.begin();for(int i=0;i<2;++i){for(int j=0;j<2;++j){matrix[p.first+i][p.second+j] ^= 1;vector<pii>added,deleted;for(int l=-1;l<1;++l){for(int m=-1;m<1;++m){if( check(p.first+i+l, p.second+j+m) && fault.find(pii(p.first+i+l, p.second+j+m))!=fault.end() ) {fault.erase(pii(p.first+i+l, p.second+j+m));deleted.push_back(pii(p.first+i+l, p.second+j+m));} else if ( !check(p.first+i+l, p.second+j+m) && fault.find(pii(p.first+i+l, p.second+j+m))==fault.end() ) {fault.insert(pii(p.first+i+l, p.second+j+m));added.push_back(pii(p.first+i+l, p.second+j+m));}}}calc(d+1);matrix[p.first+i][p.second+j] ^= 1;for(int l=0;l<added.size();++l){fault.erase(added[l]);}for(int l=0;l<deleted.size();++l){fault.insert(deleted[l]);}}}}int main() {while(scanf("%d%d%d",&n,&m,&k)!=EOF){for(int i=0;i<n;++i){for(int j=0;j<m;++j){scanf("%d",&matrix[i][j]);}}fault.clear();ans=N;for(int i=0;i<n;++i){for(int j=0;j<m;++j){if(!check(i,j)){fault.insert(pii(i,j));}}}calc(0);if(ans>k){ans=-1;}printf("%d\n",ans);}return 0;}

思路2:
经过更加深入的观察,可以发现,如果满足最终条件,那么对于任意两行
col[i] = [a[0][i], a[1][i], ... , a[n-1][i] ]
col[j] = [a[0][j], a[1][j], ... , a[n-1][j] ]
有col[i] ^ col[j] = [0,0,..,0]或者[1,1,..,1]
根据这个性质,我们将矩阵转换成n<m的n*m矩阵
如果n<=k,那么可以枚举所有可能的列的掩码(0~(1<<n)-1),
然后计算需要改变的最少元素个数,即可。
如果n>k,那么一定有其中一列会作为标准列。
(否则每一列至少改变一个元素,使得答案大于k)
这样,我们就枚举标准列,得到最优答案。

/** * @date    2014-05-09 02:43:08 * @author  jasison * @email   jasison27@gmail.com * @website http://www.jiangshan27.com */#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N = 105;int n,m,k;int a[N][N],b[N][N];int main() {while(scanf("%d%d%d",&n,&m,&k)!=EOF){int ele;for(int i=0;i<n;++i){for(int j=0;j<m;++j){scanf("%d",&ele);a[i][j]=ele;b[j][i]=ele;}}if(n>m){memcpy(a,b,sizeof(a));int t=n;n=m;m=t;}int cost=N;if(n<=k){for(int mask=(1<<n)-1;mask>=0;--mask){int cst=0;for(int j=0;j<m;++j){int cnt=0;for(int i=0;i<n;++i){if( (mask&(1<<i)?1:0 ) ^ a[i][j]){cnt++;}}cst+=min(cnt,n-cnt);}cost=min(cost,cst);}}else{for(int fix=0;fix<m;++fix){int cst=0;for(int j=0;j<m;++j){int cnt=0;for(int i=0;i<n;++i){if(a[i][j]^a[i][fix]){cnt++;}}cst+=min(cnt,n-cnt);}cost=min(cost,cst);}}if(cost>k){cost=-1;}printf("%d\n",cost);}}

425D:

给出n个点(x,y),
1<=n<=100000,0<=x<=100000,0<=y<=100000
问这n个点能够组成多少个正方形?(每个点能够重复使用)


思路:
如果枚举每一个顶点作为右上角的顶点,
然后枚举同一行的顶点作为左上角的顶点,
那么可以其他两个顶点已经可以确定,二分搜索即可。
但是,同一行的顶点会有n个,
同理,同一列的顶点也会有n个,
这样做,时间复杂度是O(n*n*2*log(n))会超时。
然后考虑,可以枚举同行和同列中较少元素的那一行或者同一列,
这样做最终的复杂度会是O(n*sqrt(n)*2*log(n))。
但是,不知道怎样证明时间复杂度的正确性。

/** * @date2014-05-09 01:35:21 * @author  jasison * @email   jasison27@gmail.com * @website http://www.jiangshan27.com */#include <cstdio>#include <vector>#include <algorithm>using namespace std;const int N = 100000;vector<int>sx[N+5],sy[N+5];vector<int>::iterator jt;int vx[N+5],vy[N+5];int n,ans;int main() {while(scanf("%d",&n)!=EOF){for(int i=0;i<=N;++i){sx[i].clear();sy[i].clear();}int rx,ry;for(int i=0;i<n;++i){scanf("%d%d",&rx,&ry);vx[i] = rx;vy[i] = ry;sx[ry].push_back(rx);sy[rx].push_back(ry);}for(int i=0;i<=N;++i){sort(sx[i].begin(),sx[i].end());sort(sy[i].begin(),sy[i].end());}ans=0;for(int i=0;i<n;++i){int cnt1=lower_bound(sx[vy[i]].begin(),sx[vy[i]].end(),vx[i]) - sx[vy[i]].begin();int cnt2=lower_bound(sy[vx[i]].begin(),sy[vx[i]].end(),vy[i]) - sy[vx[i]].begin();if(cnt1>cnt2){for(jt=sy[vx[i]].begin(); *jt<vy[i]; ++jt){int len=vy[i]-(*jt);if(vx[i]-len>=0 && binary_search(sy[vx[i]-len].begin(),sy[vx[i]-len].end(),vy[i]) && binary_search(sy[vx[i]-len].begin(),sy[vx[i]-len].end(),*jt)){ans++;}}}else{for(jt=sx[vy[i]].begin(); *jt<vx[i]; ++jt){int len=vx[i]-(*jt);if(vy[i]-len>=0 && binary_search(sx[vy[i]-len].begin(),sx[vy[i]-len].end(),vx[i]) && binary_search(sx[vy[i]-len].begin(),sx[vy[i]-len].end(),*jt)){ans++;}}}}printf("%d\n",ans);}}


官方题解?:
先枚举每一列,如果该列元素少于等于sqrt(n),
那么就枚举这一列每两个顶点作为顶点并且标记这一列的所有元素。
完成之后,将所有标记的顶点删除,再对每一行再做一遍(此时每一行的顶点个数已经少于等于sqrt(n))。

写完了WA,主要是在标记的时候没有处理好吧。

0 0
原创粉丝点击