2021-10-09 SCOI2009

生日快乐

这题看起来蛋糕可以乱切,但实际上不行,设当前矩形为长 x x x,宽 y y y,分 k k k 块,显然分出一块蛋糕长最短为 x / k x/k x/k,宽最短为 y / k y/k y/k,而且切出来的蛋糕,一定是最短长宽的倍数,不然后面的蛋糕一定不能切出来面积一样。这样直接深搜就行了。

#include<bits/stdc++.h>
#define FRE freopen("happy.in","r",stdin);\
            freopen("happy.out","w",stdout);
using namespace std;
template<typename T>inline void read(T &x){
    T ch=getchar();x=0;
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
double dfs(int k,double x,double y){
    if(k==1){return max(x/y,y/x);}
    double lx=x/k,ly=y/k,ans=99999999.9,r1,r2;
    for(int i=1;i<=k/2;++i){
        r1=max(dfs(i,lx*i,y),dfs(k-i,x-lx*i,y));
        r2=max(dfs(i,x,ly*i),dfs(k-i,x,y-ly*i));
        ans=min(ans,min(r1,r2));
    }
    return ans;
}
int main() {
    int x,y,n;
    read(x),read(y),read(n);
    printf("%.6lf",dfs(n,x,y));
    return 0;
}

最长距离

最短路转化, d i s t dist dist 数组存的是从源点到当前点最少需要搬多少石头,对所有 d i s t dist dist 小于 t t t 的数组更新答案就行了。

#include<bits/stdc++.h>
using namespace std;
int n,m,t,ans;
int dx[]={1,0,-1,0},
    dy[]={0,-1,0,1};
int dist[40][40];
bool f[40][40];
bool v[40][40];
queue<int>qx;
queue<int>qy;
void spfa(int x,int y)
{
    memset(dist,0x3f,sizeof dist);
    memset(v,0,sizeof v);
    dist[x][y]=f[x][y];v[x][y]=1;
    qx.push(x),qy.push(y);
    while(qx.size()){
        int l=qx.front(),r=qy.front();
        qx.pop(),qy.pop();
        v[l][r]=0;
        for(int i=0;i<4;++i){
            int tx=l+dx[i],ty=r+dy[i];
            if(tx<1||ty<1||tx>n||ty>m) continue;
            int tz=0;if(f[tx][ty]) tz=1;
            if(dist[tx][ty]>dist[l][r]+tz){
                dist[tx][ty]=dist[l][r]+tz;
                if(!v[tx][ty]) qx.push(tx),qy.push(ty),v[tx][ty]=1;
            }
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) 
            if(dist[i][j]<=t) 
                ans=max(ans,(i-x)*(i-x)+(j-y)*(j-y));
}
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%1d",&f[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            spfa(i,j);
    printf("%.6f",sqrt(1.0*ans));
    return 0;
}

Windy 数

  1. 数位 D P DP DP
    F [ i , j ] F[i,j] F[i,j] 表示前 i i i 位开头为 j j j 的数有多少个。易得转移方程 F [ i + 1 ] [ j ] = ∑ F [ i , k ] ( ∣ j − k ∣ ≥ 2 ) F[i+1][j]=\sum F[i,k](\left\vert j-k \right\vert \geq 2) F[i+1][j]=F[i,k](jk2)这个解决了,现在要求求 x x x 以内的 W i n d y Windy Windy 数有多少个。假设 x = 54201 x=54201 x=54201,首 先对于第一位 5 5 5 来说 19999 19999 19999 49999 49999 49999 以内的 W i n d y Windy Windy 数都要算入,然后对于下一位 4 4 4 来说 50999 50999 50999 53999 53999 53999 内满足条件的数都要算入,后面几位同理。但此时发现到后面几位时因为开头为 54 54 54 所以后面的无论怎样都满足不了,就不用考虑了若到最后都没有出现 54 54 54 这样类似的情况,例如 53130 53130 53130,说明这个数本身也是 W i n d y Windy Windy 数,要记入答案。完了吗?没有!以上讨论都是建立在为位数位 5 5 5 的情况, 9999 9999 9999 及以下的都没有考虑进去,这也要统计。
#include<bits/stdc++.h>
using namespace std;
int a,b,ans;
int f[11][11];
void pre(){
	for(int i=0;i<10;++i) f[1][i]=1;
	for(int i=1;i<=9;++i)
		for(int j=0;j<10;++j)
			for(int k=0;k<10;++k)
				if(abs(j-k)>=2) f[i+1][j]+=f[i][k];
}
int get(int a){
	int A[11];
	int ans=0,la=0;
	while(a){
		A[++la]=a%10;
		a/=10;
	}
	for(int j=1;j<A[la];j++) ans+=f[la][j];
	for(int i=la-1;i>=1;i--){
		for(int j=0;j<A[i];j++) 
			if(abs(A[i+1]-j)>1)
				ans+=f[i][j];
		if(abs(A[i+1]-A[i])<2) break;
		if(i==1) ans++;
	}
	for(int i=1;i<la;i++)
		for(int j=1;j<=9;j++) 
			ans+=f[i][j];
	return ans;
}
int main() {
	pre();
	read(a),read(b);
	if(b<10) prt(b-a+1);
	else prt(get(b)-get(a-1));
    return 0;
}
  1. 打表
    一开始做这题的时候,我本以为 2 e 9 2e9 2e9 内的 W i n d y Windy Windy 数很少,就想着跑一遍来着,但是发现挺多的,
    老实写完数位动态规划后却发现一千万以内的跑得很快,于是换了种打表方式,暴力找出每个一千万内有多少个 W i n d y Windy Windy 数,开个 200 200 200 的数组来存,对于输入的 a , b a,b a,b 若所属的一千万相同或挨在一起,就暴力跑出 a a a b b b 间有多少,否则先加上他们之间所有的一千万内的 W i n d y Windy Windy 数,再暴力跑。
#include<bits/stdc++.h>
using namespace std;
int p[201]={/*表当然自己就能打了*/};
bool check(int x){
	int a=x%10,b;
	x/=10;
	while(x){
		b=x%10;
		x/=10;
		if(abs(a-b)<2) return 0;
		a=b;
	}
	return 1;
}
int a,b,ans;
int main() {
	read(a),read(b);
	if(b<10){cout<<(b-a+1);}
	int x=a/10000000+1,y=b/10000000-1;
	if(x>y){
		for(int i=a;i<=b;++i)
			if(check(i)) ans++;
	}
	else{
		for(int i=x;i<=y;++i)
			ans+=p[i];
		for(int i=a;i<x*10000000;++i) if(check(i)) ans++;
		for(int i=(y+1)*10000000;i<=b;++i)	if(check(i)) ans++;
	}
	prt(ans);
    return 0;
}
/*
546561 56465156

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值