5.4 比赛总结

T1

题目:

这题……应该不需要我讲吧。(讲个冷笑话:作者在比赛的时候不小心多写了一个循环……然后就 W A \color{red}{WA} WA 了……(还是全 W A \color{red}{WA} WA 哦)。)

内心OS:孩子没救了……

代码自己写。

T2

题目:

这题也是非常的水……

题目中说了:所有黑色格子形成一个矩形,这句话说明了什么?这句话说明了我们需要找到这个矩形最大时的情况,那最大情况是什么呢?那自然就是从最上方、最右边到最下方、最左边。但是我们又不需要一些没用的格子变成黑色(说不定就因为这个原因又涂不满了),所以我们只需要把从黑色点 x , y x,y x,y 的最小值到 x , y x,y x,y 的最大值这个区域涂黑就行了。

代码依然非常的简单……但是我还是贴在了这:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e3+5;
ll T,a[N][N],n,m;
string s;
void solve() {
    cin>>n>>m;
    ll mnx=2e13,mny=2e13,mxx=-1,mxy=-1;
    for(ll i=1;i<=n;i++) {
        cin>>s;
        for(ll j=0;j<m;j++) {
            if(s[j]=='#') {
                a[i][j+1]=1;
                mnx=min(mnx,i);
                mxx=max(mxx,i);
                mny=min(mny,j+1);
                mxy=max(mxy,j+1);
            }
            else if(s[j]=='.') {
                a[i][j+1]=0;
            }
            else {
                a[i][j+1]=-1;
            }
        }
    }
    for(ll i=mnx;i<=mxx;i++) {
        for(ll j=mny;j<=mxy;j++) {
            if(a[i][j]==0) {
                cout<<"No"<<endl;
                return ;
            }
        }
    }
    cout<<"Yes"<<endl;
}
int main() {
    // freopen("color.in","r",stdin);
    // freopen("color.out","w",stdout);
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

T3

题目:

给大家讲一讲各分数的方法吧。

0pts

根本不会,直接不写。

50pts

直接暴力枚举圆周围的所有正方形,看看每个正方形里离圆心最远的点到圆心的距离是否小于等于半径,然后统计。

代码:

#include <bits/stdc++.h>
#define ll long long
namespace io {
	using namespace std;
	inline ll read() {
		char n=getchar();
		ll num=0,flag=1;
		while(n<'0'||n>'9') {
			if(n=='-') {
				flag=-1;
			}
			n=getchar();
		}
		while(n>='0'&&n<='9') {
			num=num*10+n-'0';
			n=getchar();
		}
		return num*flag;
	}
	inline void print(ll x) {
		if(x<0) {
			putchar('-');
			print(-x);
			return;
		}
		if(x==0) {
			return;
		}
		print(x/10);
		putchar((char)(x%10+'0'));
	}
}
using namespace io;
const ll N=1e6+5;
ll T,ans,n,m,cnt;
void solve() {
    ll i;
    scanf("%lld",&i);
    cnt=0;
    for(ll j=-i;j<=i;j++) {
        for(ll k=-i;k<=i;k++) {
            if((j+0.5)*(j+0.5)+(k+0.5)*(k+0.5)<=1.0*i*i&&(j-0.5)*(j-0.5)+(k+0.5)*(k+0.5)<=1.0*i*i&&(j+0.5)*(j+0.5)+(k-0.5)*(k-0.5)<=1.0*i*i&&(j-0.5)*(j-0.5)+(k-0.5)*(k-0.5)<=1.0*i*i) {
                cnt++;
            }
        }
    }
    if(cnt) {
        print(cnt);
    }
    else {
        cout<<0;
    }
}
int main() {
    // freopen("circle.in","r",stdin);
    // freopen("circle.out","w",stdout);
    T=1;
    while(T--) {
        solve();
    }
    return 0;
}

100pts

考虑只计算整个圆的 1 4 \cfrac{1}{4} 41,因为整个圆是一个轴对称图形,里面的正方形也正好是对称的。如下:

然后在直径上的正方形单独处理就行。

问题就变成了怎么计算每一列有多少个正方形。

通过观察,我们不难发现:每一列正方形的数量是单调不递增的,那么我们就不难想到一个东西:二分!(确实挺容易想到。)

但我告诉你,这题不用二分,而是用双指针

因为双指针的变化也是具有单调性的,它只能一直增长或者一直下降,不可能从头再来计算一遍。

所以,我们可以先定义一个指针(另一个指针就是第几列),这个指针就是当前正方形数量最多的那一列的正方形数量,那不用说,肯定在半径上。

前面我们说了:半径上的单独处理。所以这一列的正方形数量实际上是不会加上的。那怎么求这个数量呢?很简单,半径长为 r r r,上面有 0.5 0.5 0.5 在外面,下面有 0.5 0.5 0.5 在下半部分圆。所以个数就是 r − 0.5 − 0.5 = r − 1 r-0.5-0.5=r-1 r0.50.5=r1

接着就是循环。我们要循环 r − 1 r-1 r1 次,原因:半径上的不算,圆外面的不算,算下来就是 r − 1 r-1 r1次。

那我们怎么改变指针呢?这里要用到勾股定理:

我们可以通过当前的正方形个数求出竖直蓝色线段的长度,通过第几列求出横向蓝色线段的长度,然后运用勾股定理可知红色线段长度,再与绿色线段(半径)相比较,如果大于它,那么就数量减一,直到小于等于半径为止。

核心代码如下:

while(cnt*cnt+(i*1.0+0.5)*(i*1.0+0.5)*1.0>r*r*1.0) {
	cnt--;
}

其中 cnt 表示当前的高度,i 是当前是第几列,r 是半径。(一般写代码,我们并不会真的开方,即 sqrt(),而是会移到另一边变成平方。)

然后就可以写出代码了:

#include <bits/stdc++.h>
#define ll long long
namespace io {
	using namespace std;
	inline ll read() {
		char n=getchar();
		ll num=0,flag=1;
		while(n<'0'||n>'9') {
			if(n=='-') {
				flag=-1;
			}
			n=getchar();
		}
		while(n>='0'&&n<='9') {
			num=num*10+n-'0';
			n=getchar();
		}
		return num*flag;
	}
	inline void print(ll x) {
		if(x<0) {
			putchar('-');
			print(-x);
			return;
		}
		if(x==0) {
			return;
		}
		print(x/10);
		putchar((char)(x%10+'0'));
	}
}
using namespace io;
const ll N=1e6+5;
ll r,ans;
int main() {
    // freopen("circle.in","r",stdin);
    // freopen("circle.out","w",stdout);
    cin>>r;
	double cnt=r*1.0-1.0;
	for(ll i=1;i<r;i++) {
		cnt+=0.5;
		while(cnt*cnt+(i*1.0+0.5)*(i*1.0+0.5)*1.0>r*r*1.0) {
			cnt--;
		}
		cnt-=0.5;
		ans+=(ll)(cnt);
	}
	ans*=4;
	ans+=4*r-3;
	cout<<ans;
    return 0;
}

完美 A C \color{green}{AC} AC

T4

题目:

这题第一眼看上去很像二分(因为它说了最小的最大),但是二分很难写(反正我没写出来),那么我们就可以用另一个一眼能看出来的算法:01 背包!(不知道 01 背包的同学可以看这里。)

既然有三种维生素,那就定义三个背包。

求完了 01 背包,然后呢?然后就很简单了,题目中说总卡路里摄入量不超过 X X X,那我们就先暴力枚举维生素 A 的总卡路里数,再枚举维生素 B 的总卡路里数,那要最大的话维生素 C 就必然要 X − i − j X-i-j Xij i , j i,j i,j 分别表示维生素 A 的总卡路里数、维生素 B 的总卡路里数),然后要求最小的最大,那就三者求最小值,再看看这个最小值是否是最大值,如果是,更新答案。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e3+5;
struct node {
    ll id,vl,wl;
};
vector<node> vl1,vl2,vl3;
ll T,dp1[N][N],n,m,dp2[N][N],dp3[N][N],cnt1,cnt2,cnt3,ans=-1,a[N];
void solve() {
    cin>>n>>m;
    for(ll i=1;i<=n;i++) {
        ll x,vl,wl;
        cin>>x>>vl>>wl;
        if(x==1) {
            vl1.push_back({++cnt1,vl,wl});
        }
        if(x==2) {
            vl2.push_back({++cnt2,vl,wl});
        }
        if(x==3) {
            vl3.push_back({++cnt3,vl,wl});
        }
    }
    for(auto it : vl1) {
        for(ll i=0;i<=m;i++) {
            if(i<it.wl) {
                dp1[it.id][i]=dp1[it.id-1][i];
            }
            else {
                dp1[it.id][i]=max(dp1[it.id-1][i],dp1[it.id-1][i-it.wl]+it.vl);
            }
        }
    }
    for(auto it : vl2) {
        for(ll i=0;i<=m;i++) {
            if(i<it.wl) {
                dp2[it.id][i]=dp2[it.id-1][i];
            }
            else {
                dp2[it.id][i]=max(dp2[it.id-1][i],dp2[it.id-1][i-it.wl]+it.vl);
            }
        }
    }
    for(auto it : vl3) {
        for(ll i=0;i<=m;i++) {
            if(i<it.wl) {
                dp3[it.id][i]=dp3[it.id-1][i];
            }
            else {
                dp3[it.id][i]=max(dp3[it.id-1][i],dp3[it.id-1][i-it.wl]+it.vl);
            }
        }
    }
    a[0]=dp3[cnt3][0];
    for(ll i=1;i<=m;i++) {
        a[i]=max(a[i-1],dp3[cnt3][i]);
    }
    //提前做了一步处理,就是看看卡路里数能达到 m 的情况下到底多少卡路里时维生素含量最大
    for(ll i=0;i<=m;i++) {
        for(ll j=0;j<=m-i;j++) {
            ll cnt=min({dp1[cnt1][i],dp2[cnt2][j],a[m-i-j]});
            if(ans<cnt) {
                ans=cnt;
            }
        }
    }
    cout<<ans;
}
int main() {
    // freopen("vitamin.in","r",stdin);
    // freopen("vitamin.out","w",stdout);
    T=1;
    while(T--) {
        solve();
    }
    return 0;
}

总结

  1. T1:错的很冤,直接挂了 100pts,希望下次不再犯。
  2. T2:完美 A C \color{green}{AC} AC
  3. T3:真的不会,不过骗了 50pts。
  4. T4:完美 A C \color{green}{AC} AC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值