Codeforces Round #217 (Div. 2)

这场拙计在A题上浪费了点时间,居然去写bfs。。。还好没有fst。。。这场D题写得太翔了,赛后150行才过。。。


C: 


题意:小朋友们每人都有一对颜色一样的手套,分左右手。现在他们要互换手套使得左右手颜色不一样。求最多能满足多少小朋友。


思路:

        没多想,直接暴力。。。统计每种颜色左右手有多少个。然后对于某种颜色的左(右)手,给它寻找一只不同颜色的右(左)手。如果找到就记录下来,同时去除这两只手套。最后必然剩下一种颜色的手套(或者刚好不剩)。然后我们去刚刚记录的结果里寻找一双手套,看能不能和这种颜色互换,如果能就换。遍历完所有结果后,还有剩余手套的话,那剩下的只能同种颜色组合了。复杂度O(n*m)。


code:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;

#define N  100010
#define ALL(x)     x.begin(),x.end()
#define CLR(x,a)   memset(x,a,sizeof(x))
typedef long long    ll;
typedef pair<int,int> PI;
const int INF    = 0x3fffffff;
const int MOD    = 100000007;
const double EPS = 1e-7;

int cnt[N][2],n,m;
vector<PI > d;

bool find(int c,int op)
{
	for(int i=1;i<=m;i++){
		if(i==c) continue;
		if(cnt[i][op]){
			cnt[i][op]--;
			cnt[c][op^1]--;
			if(op) d.push_back((PI){c,i});
			else d.push_back((PI){i,c});
			return true;
		}
	}
	return false;
}

int main()
{
	int c;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&c);
		cnt[c][0]++; //left
		cnt[c][1]++; //right
	}
	for(int i=1;i<=m;i++){
		while(cnt[i][0] && find(i,1));
		while(cnt[i][1] && find(i,0));
	}
	c=-1;
	for(int i=1;i<=m;i++) if(cnt[i][0]){
			c=i; break;
	}
	int num=0;
	if(c!=-1){
		num=cnt[c][0];
		for(int i=0;num && i<d.size();i++)
			if(d[i].first!=c && d[i].second!=c){
				d.push_back((PI){d[i].first,c});
				d[i].first=c;
				num--;
			}
	}
	printf("%d\n",d.size());
	for(int i=0;i<d.size();i++)
		printf("%d %d\n", d[i].first,d[i].second);
	while(num>0) printf("%d %d\n",c,c), num--;
    return 0;
}



D:


题意:给一张图,上面有一些‘w’和空位‘.’ ,你需要在‘.’的地方填充‘+’符号,使得‘+’和‘w’连起来能形成最小边长的正方形框且没有多余的‘w’。题目保证至少有一个‘w’(一个‘w’算是1*1的正方形框)。


思路:

        如果我们从第一行开始扫,扫到第一个‘w’,那么这个‘w’的行高可能是方形框的上边界。同理,我们可以得出上下左右四条边界。然后的问题是,这四条边界是否能恰好组成方形框。如果能,那我们直接把边界上的字符都替换成‘+’(除了本身是‘w’的),最后再看一下有没有多余‘w’,没有的话我们就顺利找到了答案,输出即可。但是这题麻烦就麻烦在,如果不能组成方形框,那么我们需要扩张边界,使得边长达到size=max(right-left,bottom-top),此时必然是最小边长的正方形框。显然我们只需要考虑上下或者左右的扩张即可。

        不是每条边界都能扩张的,必须满足这条边界上除了端点(端点指的是当前形成的矩形框的端点)以外都没有‘w’。那么这条边界就能扩张。否则一旦扩张,那么边界上的‘w’将来就会在方框的内部,不合法。扩张好之后,把新边界上的字符都替换成‘+’,再检测下是否合法。

        但是不得不说这题为什么这么多FST就是,仅仅考虑完上述通俗情况你还会跪一组数据。。。

        对于这种“www”,退化成线段的。按我上诉的方法会认为边界不可扩张,因为边界上除了端点还多了一个的'w'。但事实上我们可以把“www”作为底边扩张出3*3的方框。所以还需要特判两条边界如果重合,即使边界上有'w',我们依然可以固定一条边界,扩张另外一条边界形成方框。所以这种退化成线段的情况不需要考虑是否能扩张边界,固定一条边界,另外一条自然能扩张,区别就在于不能两条边界同时扩张。


        代码略长,不过很多都是复制的。细节就耐心看吧。。。


code:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;

#define N  100010
#define ALL(x)     x.begin(),x.end()
#define CLR(x,a)   memset(x,a,sizeof(x))
typedef long long    ll;
typedef pair<int,int> PI;
const int INF    = 0x3fffffff;
const int MOD    = 100000007;
const double EPS = 1e-7;

char mp[2012][2012],tmp[2012][2012];
int n,m;
vector<int> d;
vector<PI > r;

bool check()
{
	memcpy(tmp,mp,sizeof(mp));
	for(int i=d[2];i<=d[3];i++) mp[d[0]][i]='+';
	for(int i=d[2];i<=d[3];i++) mp[d[1]][i]='+';
	for(int i=d[0];i<=d[1];i++) mp[i][d[2]]='+';
	for(int i=d[0];i<=d[1];i++) mp[i][d[3]]='+';
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++) if(mp[i][j]=='w'){
			memcpy(mp,tmp,sizeof(mp));
			return false;
		}
	for(int i=0;i<r.size();i++)
		mp[r[i].first][r[i].second]='w';
	for(int i=0;i<n;i++) printf("%s\n", mp[i]);
	return true;
}

void solve(int op)
{
	char c;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++){
			c=op?mp[i][j]:mp[j][i];
			if(c=='w'){
				d.push_back(i);
				i=n;
				break;
			}
		}
	for(int i=n-1;i>=0;i--)
		for(int j=m-1;j>=0;j--){
			c=op?mp[i][j]:mp[j][i];
			if(c=='w'){
				d.push_back(i);
				return ;
			}
		}
}

int main()
{
	
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%s",mp[i]);
		for(int j=0;j<m;j++)
			if(mp[i][j]=='w') r.push_back((PI){i,j});
	}

	solve(1);
	swap(n,m);
	solve(0);
	swap(n,m);

	int size=max(d[1]-d[0],d[3]-d[2]);
	bool o1,o2;
	if(d[1]-d[0]!=size){
		int dif=size - (d[1]-d[0]);
		if(d[0]==d[1]){
			if(d[0]>=dif){
				d[0]-=dif;
				if(check()) return 0;
				d[0]+=dif;
			}	
			if(n-1-d[1]>=dif){
				d[1]+=dif;
				if(check()) return 0;
				d[1]-=dif;
			}
		}
		o1=true;
		for(int i=d[2]+1;i<d[3];i++)
			if(mp[d[0]][i]=='w') o1=false;
		o2=true;
		for(int i=d[2]+1;i<d[3];i++)
			if(mp[d[1]][i]=='w') o2=false;
		if(o1 || o2){
			int Min=min(dif,d[0]);
			if(o1) d[0]-=Min, dif-=Min;
			if(o2 && dif){
				Min=min(n-1-d[1],dif);
				d[1]+=Min;
				dif-=Min;
			}
			if(!dif && check()) return 0;
		}	
	}else if(d[3]-d[2]!=size){
		int dif=size - (d[3]-d[2]);
		if(d[2]==d[3]){
			if(d[2]>=dif){
				d[2]-=dif;
				if(check()) return 0;
				d[2]+=dif;
			}	
			if(m-1-d[3]>=dif){
				d[3]+=dif;
				if(check()) return 0;
				d[3]-=dif;
			}
		}
		o1=true;
		for(int i=d[0]+1;i<d[1];i++)
			if(mp[i][d[2]]=='w') o1=false;
		o2=true;
		for(int i=d[0]+1;i<d[1];i++)
			if(mp[i][d[3]]=='w') o2=false;
		if(o1 || o2){
			int Min=min(dif,d[2]);
			if(o1) d[2]-=Min, dif-=Min;
			if(o2 && dif){
				Min=min(m-1-d[3],dif);
				d[3]+=Min;
				dif-=Min;
			}
			if(!dif && check()) return 0;
		}
	}else if(check()) return 0;
	puts("-1");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值