2022.3.5

Codeforces Round #774 (Div. 2) A-C

A

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+10,INF=1e8;
int a[N];
int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		ll n,s;
		scanf("%lld%lld", &n,&s);
		n *= n;
		printf("%lld\n", s / n);
	}
	return 0;
}

B

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+10,INF=1e8;
ll a[N];
int main()//红名大佬的题解
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
        int n,f=0;
        scanf("%d", &n);
        for (int i = 1; i <= n;i++)
        {
            scanf("%lld", &a[i]);
        }
        sort(a + 1, a + 1 + n);
        ll l = 2, r = n, ans = a[n] - a[1] - a[2];
        while(l<r)
        {
            if(ans>0) {
                f = 1;
                break;
            }
            ans += a[--r] - a[++l];
        }
        if(f)
            printf("YES\n");
        else
            printf("NO\n");
    }
	return 0;
}
/*我的题解
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+10,INF=1e8;
ll a[N],s[N];
int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		int n;
		ll sum = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n;i++)
		 	s[i] = 0;
		for (int i = 1; i <= n  ;i++)
		{
			scanf("%lld", &a[i]);
		}
		sort(a + 1, a + 1 + n);
		for (int i = 1; i <= n;i++)
		{
			s[i] = s[i - 1] + a[i];
		}
		int k = n / 2;
		if(n%2==0)
		{
			int i = n - 1,f=0;
			for (int j = 2; j <= k + 1; j++)
			{
				if(s[n]-s[i]<=s[j])
				{
					if(i>k+1) i--;
					else
					{
						printf("NO\n");
						break;
					}
				}
				else
				{
					printf("YES\n");
					break;
				}
			}
		}
		else
		{
			if(s[n]-s[k+1]>s[k+1])
				printf("YES\n");
			else
				printf("NO\n");
		}
	}
	return 0;
}

*/

C

参考知乎,位运算的使用,对于每个数可以枚举到2的15次方,然后用n=1!+2!+3!...+t表示,最后把t拆成2的几次幂,即算一下t里有几个1加一起取最小值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
ll fac[20];
int main()
{
	int t;
	ll res = 1;
	scanf("%d", &t);
	for (int i = 1; i <= 15;i++)
	{
		res *= i;
		fac[i - 1] = res;
	}
	while (t--)
	{
	    ll n;
	    int ans = 1000;
	    scanf("%lld", &n);
	    for (int i = 0; i < 1 << 15; i++)
	    {
		bitset<15> b(i);
	        ll t = n;
		for (int j = 0; j < 15;j++)
		    t -= fac[j] * b[j];
		if(t>=0)
		{
			bitset<64> b1(t);
			ans = min(ans,(int)(b1.count() + b.count()));
		}
	    }
	printf("%d\n", ans);
	}
	return 0;
}


蓝书

AcWing 91. 最短Hamilton路径

利用状态压缩dp降低空间复杂度,我们定义dp[i][j],其中i为二进制表示,某一位为1表示已经走过了,0位没走过,j为当前处于哪个点,那么我们最后要求的就是dp[(1<<20)-1][n-1],即每一个点都走过了并且已经走到终点的路径数。需要初始化一下dp[1][0]=0,即当前已经在0点并且走了1个点。于是在任意的时刻,dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+w[k][j]),异或是因为当前转态和上一个状态只能有一个点在j,然后对于每一位是1的位k,我们可以由k转移到j,于是记录一下转移的最小值,最后即可得到最优解。当时一直没太明白本题dp的定义,现在回过头来看比之前清晰很多。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int w[25][25], a[1<<20][25];
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n;i++)
        for (int j = 0; j < n;j++)
            scanf("%d", &w[i][j]);
    for (int i = 0; i < 1 << n;i++)
        for (int j = 0; j < 25;j++)
            a[i][j] = INF;
    a[1][0] = 0;
            for (int i = 0; i < 1 << n; i++)
                for (int j = 0; j < n; j++)
                {
                    if (i >> j & 1)
                    {
                        for (int k = 0; k < n; k++)
                        {
                            if (i - (1 << j) >> k & 1)
                                a[i][j] = min(a[i][j], a[i-(1<<j)][k] + w[k][j]);
                        }
                    }
                }
    printf("%d\n", a[(1 << n) - 1][n - 1]);
    return 0;
}

AcWing 998. 起床困难综合症

本题是让我们在[0,m]之间选择一个数x0,使得经过n次位运算,结果ans最大。那么我们可以枚举x0的每一位看填1还是0更优。对于某一位k来说,尽量填1,当且仅当满足,k前面已经填好的更高位加上1<<k不会超过m,并且用第k位进行n次位运算,如果k=1,n次位运算之后仍为1,如果k=0,n次位运算之后仍为0,这时肯定是k=1,更优。于是我们枚举k的每一位后就可以得到最优解

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int n, m, t[N], op[N];
int get(int k,int now)
{
    for (int i = 1; i <= n;i++)
    {
        int x = t[i] >> k & 1;
        if(op[i]==1)
            now &= x;
        else if(op[i]==2)
            now |= x;
        else if(op[i]==3)
            now ^= x;
    }
    return now;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n;i++)
    {
        char s[5];
        scanf("%s%d", s, &t[i]);
        if(s[0]=='A')
            op[i] = 1;
        else if(s[0]=='O')
            op[i] = 2;
        else if(s[0]=='X')
            op[i] = 3;
    }
    int res = 0, ans = 0;
    for (int i = 29; i >= 0;i--)
    {
        int res0 = get(i, 0);
        int res1 = get(i, 1);
        if(res+(1<<i)<=m&&res0<res1)
        {
            res += 1 << i;
            ans += res1 << i;
        }
        else
            ans += res0 <<= i;
    }
    printf("%d\n", ans);
    return 0;
}

AcWing 95. 费解的开关

对于第一行我们可以枚举每一位的状态,一共是2^5个状态,当某一位是1时我们就按下当前这一位,注意:这里枚举的只是第一行按开关的32种方式,和输入的第一行无关。然后我们固定这一行不动,只是通过该行的下一行来改变它的状态,于是我们可以枚举2-4行,最后检查一下如果第5行还有开关是0的话说明该方法是不合法的,在合法的方法里取最小值得到最优解。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=10,INF=1e8;
int n;
char a[N][N];
int dx[5] ={0,1,-1,0,0},dy[5] = {0, 0, 0, 1, -1};
void change(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int a1=x+dx[i],b1=y+dy[i];
        if(a1>=0&&a1<5&&b1>=0&&b1<5)
        {
            if(a[a1][b1]=='1') a[a1][b1]='0';
            else a[a1][b1]='1';
        }
    }
}
int slove()
{
    int ans = INF;
    for (int i = 0; i < 1 << 5;i++)
    {
        int res = 0,ok=1;
        char back[N][N];
        memcpy(back, a, sizeof a);
        for (int j =0; j < 5;j++)
        {
            if(i>>j&1)
            {
                res++;
                change(0, j);
            }
        }
        for (int j = 0; j < 4;j++)
        {
            for (int k = 0; k < 5;k++)
            {
                if(a[j][k]=='0')
                {
                    res++;
                    change(j + 1, k);
                }
            }
        }
        for (int j = 0; j < 5;j++)
        {
            if(a[4][j]=='0')
            {
                ok = 0;
                break;
            }
        }
        if(ok) ans=min(ans,res);
        memcpy(a, back, sizeof back);
    }
    if(ans>6)
        ans = -1;
    return ans;
}
int main()
{
    int n;
    scanf("%d", &n);
    while(n--)
    {
        for (int i = 0; i < 5; i++)
            cin >> a[i];
        printf("%d\n", slove());
    }
    return 0;
}
posted @ 2022-03-07 18:10  menitrust  阅读(27)  评论(0编辑  收藏  举报