SDU_week16_CSP模测(T4区间dp)

T1 数鸭子

题目描述

在这里插入图片描述

题目分析

题目问给定的N个数,假设每个数a位,问这a位中不重复的小于K,这样的数有多少个。
解决方法是取模+右移,直到移没为止。
注意最后几个数据点的k小于1e6是唬人的!k不超过10。

长长记性

  1. 若使用STL的set去重,复杂度是alogaN,而使用数组做桶只有a*N,很容易卡长而掉分(我就这样白白损失了40分)
  2. 对于此题,STL关同步904ms,数组关同步249ms,不关同步都会T,所以大量数据记得关同步~

代码

#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
using namespace std;

int main()
{
    _;//STL关同步904ms,数组关同步249ms,不关同步都会T
    int n,k;
    cin>>n>>k;
    long long ai;
    // set<int> tong; 会TLE,不要卡常,吸取教训,不要超过1e7
    int tong[10];
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        // tong.clear();
        memset(tong,0,sizeof tong);
        cin>>ai;
        while (ai!=0)
        {
            // tong.insert(ai%10);
            tong[ai%10]++;
            ai/=10;
        }
        int temp=0;
        for(int i=0;i<=9;i++)
        {
            if(tong[i]!=0)temp++;
        }
        if(temp<k)sum++;
    }
    cout<<sum;
    return 0;
}

T2 ZJM要抵御宇宙射线

题目描述

在这里插入图片描述

题目分析

这道题是说给定平面上一堆整数点,从里面选一个作为圆心,使得所有点都被包住,使得半径平方最小。观察数据最大的才1k,完全可以n^2暴力去做(T2不要难为自己),外循环找圆心,维护内循环输出的最小的;内循环找该圆心距离所有点最远值,维护一个最大量。即最大值最小问题。

长长记性

慎用pow,他返回值是double容易掉精度,比如int(pow(3,2)+pow(4,2))可能为4.9999999=4。

代码

#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const long long inf=0x3f3f3f3f3f3f3f3f;
struct p
{
    ll x,y;
}pointt[1009];

int main()
{
    int n;
    cin>>n;
    ll dis;
    ll X,Y;
    ll temp;
    for(int i=1;i<=n;i++)
        cin>>pointt[i].x>>pointt[i].y;

    //慎用pow,他返回值是double容易掉精度
    dis = inf;
    X=pointt[1].x;
    Y=pointt[1].y;
    for(int i=1;i<=n;i++)
    {
        ll thiss = (pointt[i].x-pointt[1].x)*(pointt[i].x-pointt[1].x)+(pointt[i].y-pointt[1].y)*(pointt[i].y-pointt[1].y);
        ll thissX=pointt[i].x;
        ll thissY=pointt[i].y;
        for(int j=1;j<=n;j++)
        {
            if(i==j)continue;
            temp=(pointt[i].x-pointt[j].x)*(pointt[i].x-pointt[j].x)+(pointt[i].y-pointt[j].y)*(pointt[i].y-pointt[j].y);
            if(temp > thiss)
            {
                thiss=temp;
                thissX=pointt[i].x;
                thissY=pointt[i].y;
                continue;
            }
            if(temp == thiss)
            {
                if(pointt[i].x<thissX)
                {
                    thissX=pointt[i].x;
                    thissY=pointt[i].y;
                    continue;
                }
                if(pointt[i].x==thissX&&pointt[i].y<thissY)
                {
                    thissX=pointt[i].x;
                    thissY=pointt[i].y;
                    continue;
                }
            }
        }
        if(thiss < dis)
        {
            dis=thiss;
            X=thissX;
            Y=thissY;
            continue;
        }
        if(dis == thiss)
        {
            if(thissX<X)
            {
                X=thissX;
                Y=thissY;
                continue;
            }
            if(X==thissX&&thissY<Y)
            {
                X=thissX;
                Y=thissY;
                continue;
            }
        }
    }
    printf("%.2lf",(double)X);
    printf(" ");
    printf("%.2lf",(double)Y);
    printf("\n");
    printf("%.2lf",(double)dis);
    return 0;
}

T4 宇宙狗的危机(区间dp)

题目描述

在这里插入图片描述
在这里插入图片描述

题目分析

题目问给定的一堆升序数是否可以构成一棵二叉搜素数,使得父子之间是gcd关系。

这是一道区间dp的问题,藏得很深(也可能是我太菜)。二叉搜素树的性质是根左侧比根小,根右侧比根大。令f[i][j]为1表示区间i~j可以构成一棵二叉搜索树,为0表示不能形成二叉搜索树,f[i][k]和f[k][j]能构成二叉搜索树,则f[i][j]能构成二叉搜索树。第一层循环枚举区间长度,然后枚举区间的起始位置,根据区间长度得到区间的结束位置,最后枚举区间中的根节点。

p.s.本题需要记忆化剪枝,首先算出所有数之间是否存在gcd关系,存在cangcd的二维数组中。

代码

#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
using namespace std;

int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}

int node[709];
bool f[709][709];//f[i][j]为1表示区间i~j可以构成一棵二叉搜索树,为0表示不能形成二叉搜索树,信息存在cangcd中
// f[i][k]和f[k][j]能构成二叉搜索树,则f[i][j]能构成二叉搜索树
bool cangcd[709][709];//记忆化,防止重复递归计算
bool l[709][709],r[709][709];//令l[i][j]表示以j为根,j的左子树可到i这样的BST是否存在,r[i][j]表示以i为根,i的右子树可到j这样的BST是否存在

int main()
{
    int T;
	cin>>T;
	while(T--)
	{
		memset(f,0,sizeof f);
        memset(cangcd,0,sizeof cangcd);
		memset(l,0,sizeof l);
		memset(r,0,sizeof r);

        int n;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>node[i];
			l[i][i]=r[i][i]=1;
		}
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=n;j++)
		        if(i==j) continue;
		        else if(gcd(node[i],node[j])>1) cangcd[i][j]=true;

		for(int len=0;len<n;len++)
            for(int i=1;i+len<=n;i++)
            {
                int j=i+len;
                for(int k=i;k<=j;k++)
                    if(l[i][k]&&r[k][j])
                    {
                        f[i][j]=1;
                        if(cangcd[i-1][k]) r[i-1][j]=1;//i-1为根最远可达j
                        if(cangcd[k][j+1]) l[i][j+1]=1;//j+1为根最远可达i
                    }
            }
            if(f[1][n]) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值