(赛后补题)G - Occupy the Cities(贪心+二分)两种方法实现

纯纯大怨种是我,虽然这题两种方法都可以实现,但是二分的代码量很少,二十分钟就能写出来,而且很好调试,贪心就......写了将近4个小时才调出来的贪心写法,需要想的非常仔细,建议大家直接写二分吧!

这里还是两种写法都敲一下,毕竟是自己四个小时写出来的。

JB is playing a game. There are nn cities in the game, numbered as 1, 2, \cdots, n1,2,⋯,n. The ii-th city and the jj-th city are adjacent if and only if i = j - 1i=j−1 or i = j + 1i=j+1. Initially, some of the cities are occupied by JB.

The game runs in rounds. At the beginning of a round, each occupied city can mark at most one adjacent unoccupied city as the target of attack. At the end of the round, all the attack targets marked become occupied. The game ends when all the cities are occupied.

JB wants to occupy all the cities in minimum rounds. Can you help him?

Input

There are multiple test cases. The first line of the test case contains a positive integer TT, indicating the number of test cases. For each test case:

The first line contains an integer nn (1 \le n \le 10^61≤n≤106), indicating the number of cities.

The next line contains a string ss of length nn. It's guaranteed ss only contains '0' and '1'. The ii-th character describes the initial state of the ii-th city: if s_i =si​= '1', the ii-th city is occupied by JB initially. Otherwise, the ii-th city is not occupied initially.

It's guaranteed that the sum of nn over all the test cases doesn't exceed 10^6106. It's also guaranteed that there is at least one '1' in ss.

题意,给你一串01序列,0表示当前位置没有被攻占,1表示当前位置可以被攻占,每分钟可以让每个1选择一个方向攻占一个0,问你最短时间把所有的都变成1;

思路:首先是二分,二分需要的时间,然后判断这个时间是否所有的0都可以被攻占,最主要的就是这个check函数了, 我们需要提前把所有0的左右两个方向离他最近的第一个1的位置记录一下,然后就可以开始判断了, 如果两边的1都满足条件,首先选择左边的。贪心策略因为右边的要让给其他的0来选。

上代码

#include <bits/stdc++.h>
using namespace std;
const int N=2000010;
char a[N];
int l[N],r[N];
int n;
int f[N];
bool check(int x)
{
	for(int i=1;i<=n;i++)
	f[i]=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='1')
		continue;
		if(x>=min(i-l[i],r[i]-i)+1)
		continue;
		if(l[i]>=1&&l[i]<=n&&!f[l[i]]&&x>=i-l[i])
		{
			f[l[i]]=1;
			continue;
		}
		else if(r[i]>=1&&r[i]<=n&&!f[r[i]]&&x>=r[i]-i)
		{
			f[r[i]]=1;
			continue;
		}
		return  false;
	}
	return true;
}
int erfen(int l,int r)
{
	while(l<r)
	{
		int mid=l+r>>1;
		if(check(mid))
		r=mid;
		else l=mid+1;
	}
	return l;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		scanf("%d",&n);
		getchar();
		for(int i=1;i<=n;i++)
		{
			scanf("%c",&a[i]);
		}
		int now=-1e9;
		for(int i=1;i<=n;i++)
		{
			l[i]=now;
			if(a[i]=='1') now=i;
		}
		now=1e9;
		for(int i=n;i>=1;i--)
		{
			r[i]=now;
			if(a[i]=='1') now=i;
		}
		printf("%d\n",erfen(0,n));
	}
	return 0;
}

 贪心:

其实思路和二分一样,只不过变成了计算最短时间的过程。

我这里写的是先分块,将连续的1和连续的0都分成单个元素。

本质都一样的

然后要先特判全都是1的情况,直接输出0;

#include <bits/stdc++.h>
using namespace std;
const int N = 1001010;
char a[N];
int n0[N];
int n1[N];
int main()
{
    int n;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        getchar();
        int flag = 0;
        int p = 0;
        int q = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%c", &a[i]);
            if (a[i] == '0')
                flag = 1;
        }
        if (flag == 0)
        {
            printf("0\n");
            continue;
        }
        int cnt0 = 0, cnt1 = 0, num = 0;
        flag = -1;
        if (a[1] == '0')
            n1[++cnt1] = 0;
        for (int i = 1; i <= n; i++)
        {
            if (a[i] == '0')
            {
                if (flag == 0 || flag == -1)
                {
                    flag = 0;
                    num++;
                }
                else if (flag == 1)
                {
                    flag = 0;
                    n1[++cnt1] = num;
                    num = 1;
                }
            }
            else
            {
                if (flag == 1 || flag == -1)
                {
                    num++;
                    flag = 1;
                }
                else if (flag == 0)
                {
                    flag = 1;
                    n0[++cnt0] = num;
                    num = 1;
                }
            }
        }
        if (flag == 0)
            n0[++cnt0] = num;
        else if (flag == 1)

            n1[++cnt1] = num;

        if (a[n] == '0')
            n1[++cnt1] = 0;
        for (int i = 1; i <= cnt1; i++)
        {
            if (i == 1 && n1[i] >= 1)
                n0[i]--;
            else if (i == cnt1 && n1[i] >= 1)
                n0[i - 1]--;
            else if (n1[i] > 1)
            {
                n0[i - 1]--;
                n0[i]--;
            }
        }
        for (int i = 2; i < cnt1; i++)
        {
            if (n1[i] != 1)
                continue;
            if (i == 2 && n1[1] == 0 && n1[i + 1] != 0)
            {
                if (n0[1] >= (n0[2] + 1) / 2)
                    n0[1]--;
                else
                    n0[2]--;
                continue;
            }
            else if (i == cnt1 - 1 && n1[cnt1] == 0 && n1[i - 1] != 0)
            {
                if ((n0[i - 1] + 1) / 2 >= n0[cnt1 - 1])
                    n0[i - 1]--;
                else
                    n0[cnt1 - 1]--;
                continue;
            }
            if (n1[i] == 1)
            {
                if (n0[i - 1] >= n0[i])
                    n0[i - 1]--;
                else
                    n0[i]--;
            }
        }
        int maxx = 0;
        if (n1[1] == 0)
            maxx = max(maxx, n0[1]);
        else
            maxx = max(maxx, (n0[1] + 1) / 2);
        if (n1[cnt1] == 0)
            maxx = max(maxx, n0[cnt1 - 1]);
        else
            maxx = max(maxx, (n0[cnt1 - 1] + 1) / 2);
        for (int i = 2; i <= cnt1 - 2; i++)
            maxx = max(maxx, (n0[i] + 1) / 2);
        printf("%d\n", maxx + 1);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值