Codeforces Round #806 (Div. 4)

Codeforces Round #806 (Div. 4)

E.Mirror Grid

题意

给你一个只包含0和1的边长为n正方形网格,你可以将网格上的0转化为1或者1转化为0,使得将正方形旋转 9 0 o , 18 0 o , 27 0 o , 0 o 90^o,180^o,270^o,0^o 90o,180o,270o,0o正方形不变,问你最少的操作次数。

数据范围

1 ⩽ n ⩽ 100 1 \leqslant n \leqslant 100 1n100

考点

模拟,思维

思路

我们可以发现,旋转过程中原坐标为 ( x , y ) (x, y) (x,y)的网格顺时针旋转 9 0 o 90^o 90o会旋转到 ( y , n − x + 1 ) ( 下 标 从 1 开 始 ) (y, n - x + 1)(下标从1开始) (y,nx+1)(1),旋转四次又重新回到 ( x , y ) (x, y) (x,y)。所以我们只需要保证每个格子对应的旋转后的三个格子上的数字一样即可。

代码

void solve()
{
	scanf("%d",&n);
	mp.clear();
	for(int i = 1; i <= n; i ++ )
		scanf("%s", s[i] + 1);
	int ans = 0;
	for(int i = 1; i <= n; i ++ ){
		for(int j = 1; j <= n; j ++ ){
			if(mp.find({i, j}) == mp.end()){
				int sum = 0;
				int x = i, y = j;
				for(int k = 1; k <= 4; k ++ ){
					int newi = y, newj = n + 1 - x;
					x = newi, y = newj;
					mp[{newi, newj}] = 1;
					if(s[newi][newj] == '1') sum ++;
				}
				ans += min(4 - sum, sum);
			}
		}
	}
	cout << ans << endl;
}

G. Good Key, Bad Key

题意

有n个宝箱,你需要按1~n顺序打开所有宝箱每个宝箱里面有一定数量的金币,每次开箱后能获得其中的金币,每次开箱有两种钥匙可以选择,好钥匙和坏钥匙,如果是好钥匙,你需要花费k元买钥匙。如果是坏钥匙,不需要花费购买钥匙的钱,但后面的所有的宝箱中的金币数量减半,问你开完所有箱子后你获得金币的最大价值。

考点

dp,贪心

数据范围

1 ⩽ n ⩽ 1 0 5 , 1 ⩽ k ⩽ 1 0 9 , 1 ⩽ a i ⩽ 1 0 9 1 \leqslant n \leqslant 10^5, 1 \leqslant k \leqslant 10^9, 1 \leqslant a_i \leqslant 10^9 1n105,1k109,1ai109

思路

我们可以发现如果选择了超过29次坏钥匙,后面的所有的金币都为0,那么后面的可以都用坏钥匙开,保证花费最小。

我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示到第 i i i个箱子为止,一共使用了 j j j次坏钥匙的产生的最大贡献,我们可以吧集合分为两种,

  • 当前宝箱用坏钥匙开 d p [ i − 1 ] [ j − 1 ] + a [ i ] / p o w 2 [ j ] dp[i - 1][j - 1] + a[i] / pow2[j] dp[i1][j1]+a[i]/pow2[j]
  • 当前宝箱不用坏钥匙开 d p [ i − 1 ] [ j ] + a [ i ] / p o w 2 [ j ] − k dp[i - 1][j] + a[i] / pow2[j] - k dp[i1][j]+a[i]/pow2[j]k
  • 两者取最大值即可

d p dp dp数组的初始化

本题可以赊账,所以一开始 d p dp dp数组要初始化为 − ∞ -\infty

d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] = 0

代码

int n, m;
int a[N];
LL dp[N][35];//开第i个盒子用了j个bad钥匙能obtain的maximum
int pow2[N];

void solve()
{
	scanf("%d %d",&n, &m);
	for(int i = 1; i <= n; i ++ ) scanf("%d",&a[i]);
	for(int i = 0; i <= n + 1; i ++ ) for(int j = 0; j <= 30; j ++ ) dp[i][j] = -1e18;
	dp[0][0] = 0;
	LL ans = 0;
	for(int i = 1; i <= n; i ++ )
		for(int j = 0; j <= 30; j ++ ){
			if(j == 0) dp[i][j] = max(dp[i - 1][0] + a[i] - m, dp[i][j]);
			else{
				dp[i][j] = max({dp[i - 1][j - 1] + a[i] / pow2[j], dp[i - 1][j] + a[i] / pow2[j] - m});
			}
			ans = max(dp[i][j], ans);
		}
	printf("%lld\n",ans);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值