Codeforces Round #297 (Div. 2) C,D,E

C - Ilya and Sticks N根木棍拼成的多个矩形,是面积最大,每根木棍最多可减去1的长度(5->4).sort下所以木棍然后贪心从长往短找就可以了

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 100010;

ll a[N];

int main()
{
	ll n;
	while( cin >> n )
	{
		ll ans = 0;
		for( int i = 1; i <= n; ++i )
			cin >> a[i];
		sort( a+1, a+1+n );
		ll cur = 0;
		for( int i = n-1; i >= 1; --i )
		{
			if( a[i+1] - a[i] <= 1 )
			{
				if( cur )
					ans += a[i] * cur, cur = 0;
				else
					cur = a[i];
				i--;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

D - Arthur and Walls 给出n*m的矩阵,求最少移除的×,使剩下的所有的‘.’都可以构成矩阵

有这么一种想法,假设某个点为×,那么如果该点的上面和左边都是'.',那么只要把这个点变成'.'就行了,但实际上这样不对,我们以1表示*,0表示'.',那么有一下这种情况

00100

00100

11011

00100

00100

在上面这种情况中无需改变任意的×。

换个角度想,在任意一个2*2的小矩阵中,假设某点在左下角,它的的上,右上,右都是'.',那么该点一定要改成'.',但是改变之后它会影响到周围的八个点,那么依次判断周围的八个点,只要是×那就扔进队列里迭代更新就行了

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 2002;

char a[N][N];
int n, m;
queue < pair<int, int> > q;
int dir[8][2] = {
	1, 0, -1, 0, 0, 1, 0, -1, 1, 1, 1, -1, -1, 1, -1, -1
};

bool check(int x, int y)
{
	if(a[x][y] == '.' || x < 1 || y < 1 || x > n || y > m)
		return 0;
	if(a[x][y - 1] == '.' && a[x - 1][y - 1] == '.' && a[x - 1][y] == '.')
		return 1;
	if(a[x][y + 1] == '.' && a[x - 1][y + 1] == '.' && a[x - 1][y] == '.')
		return 1;
	if(a[x][y - 1] == '.' && a[x + 1][y - 1] == '.' && a[x + 1][y] == '.')
		return 1;
	if(a[x][y + 1] == '.' && a[x + 1][y + 1] == '.' && a[x + 1][y] == '.')
		return 1;
	return 0;
}

int main()
{
	while(~scanf("%d%i", &n, &m))
	{
		while( !q.empty() )
			q.pop();
		for( int i = 1; i <= n; ++i )
			scanf("%s", a[i] + 1 );
		for( int i = 1; i <= n; ++i )
		{
			for( int j = 1; j <= m; ++j )
			{
				if( check( i, j ) )
					q.push( make_pair(i, j) );
			}
		}
		while( !q.empty() )
		{
			int x = q.front().first;
			int y = q.front().second;
			q.pop();
			if( !check(x, y) )
				continue;
			a[x][y] = '.';
			for( int i = 0; i < 8; ++i )
			{
				int xx = x + dir[i][0];
				int yy = y + dir[i][1];
				if( check(xx, yy) )
					q.push(make_pair( xx, yy) );
			}
		}
		for( int i = 1; i <= n; ++i )
			puts(a[i]+1);
	}
	return 0;
}
E - Anya and Cubes n个数,最多可有k次操作,及使且最多使某个数变成原来的阶乘,问在不超过k次操作的情况下,选出的部分或者全部数的和为s。

注意到n最大只有25,而每个数最多有三种状态:不取,取原来的值,取其阶乘。那么一共是要3^25次状态,显然太大。这里用了一种叫折半枚举的方法,在dfs时先预处理出前一半数字的所有状态,然后在后一半的状态中查询是否有满足(前一半状态+后一半状态=答案)的情况出现,有就加入到方案数里面。详见代码。(注意LL,没看到wa了好几发)

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 40;

ll fac[N], a[N];
map <int, ll> mp[N];
ll ans;
int n, k;
ll s;

void dfs( int pos, int usd, ll sum )
{
	if( usd > k || sum > s )
		return;
	if( pos == n/2 )
	{
		++mp[usd][sum];
		return;
	}
	dfs( pos+1, usd, sum );
	dfs( pos+1, usd, sum + a[pos] );
	if( a[pos] <= 18 )
		dfs( pos+1, usd+1, sum + fac[ a[pos] ] );
}

void dfs1( int pos, int usd, int sum )
{
	if( usd > k || sum > s )
		return;
	if( pos == n )
	{
		for( int i = 0; i + usd <= k; ++i )
		{
			if( mp[i].count( s-sum ) )
				ans += mp[i][s-sum];
		}
		return;
	}
	dfs1( pos+1, usd, sum );
	dfs1( pos+1, usd, sum + a[pos] );
	if( a[pos] <= 18 )
		dfs1( pos+1, usd+1, sum + fac[ a[pos] ] );
}

int main()
{
	fac[0] = fac[1] = 1;
	for( int i = 2; i <= 18; ++i )
		fac[i] = i * fac[i-1];
	while(~scanf("%d%d%I64d", &n, &k, &s))
	{
		for( int i = 0; i <= 30; ++i )
			mp[i].clear();
		for( int i = 0; i < n; ++i )
			cin >> a[i];
		dfs( 0, 0, 0 );
		ans = 0;
		dfs1( n/2, 0, 0 );
		cout << ans << endl;
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值