做题小记·基础算法
重生之我是普及组选手
题目大多来源于 lnsyzx oj基础算法
递推篇
P51. 神、上帝以及老天爷
错位排列, 易得 \(f_i=(i-1)*(f_{i-1}+f_{i-2})\) 。
总方案数为 \(n!\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
long long a[25],f[25];
int main(){
int c;
scanf("%d",&c);
a[0]=1;
f[1]=0,f[2]=1,f[3]=2;
for(int i=1;i<=20;i++){
a[i]=a[i-1]*i;
}
for(int i=4;i<=20;i++){
f[i]=(i-1)*(f[i-1]+f[i-2]);
}
while(c--){
int n;
scanf("%d",&n);
printf("%.2lf%\n",f[n]*100.0/a[n]);
}
return 0;
}
P52. 考新郎
这个也是错排,答案是 \(C_n^m *f_m\) 。
组合数 \(n^2\) 求 : \(C_n^m = C_{n-1}^{m-1} + C_{n-1}^m\) 。实际意义来理解就是在这 \(n\) 个数里面随便找一个 \(a\) ,抽出来的数据包含 \(a\) 有 \(C_{n-1}^{m-1}\) 种,不包含 \(a\) 有 \(C_{n-1}^m\) 种。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
long long c[25][25],f[25];
int main(){
int t;
scanf("%d",&t);
f[1]=0,f[2]=1,f[3]=2;
c[0][0]=1;
for(int i=1;i<=20;i++){
c[i][0]=1;
for(int j=1;j<=i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
}
for(int i=4;i<=20;i++){
f[i]=(i-1)*(f[i-1]+f[i-2]);
}
while(t--){
int n,m;
scanf("%d%d",&n,&m);
printf("%lld\n",f[m]*c[n][m]);
}
return 0;
}
P54. 走路
设 \(f_{i,j}\) 为第 \(i\) 步由 \(j\) 方向(0上,1左,2右)上来的方案数。
转移看代码。
点击查看代码
#include <bits/stdc++.h>
#define mod 12345
using namespace std;
int f[1005][3];
int main(){
int n;
f[1][0]=f[1][1]=f[1][2]=1;
for(int i=2;i<=1000;i++){
f[i][0]=(f[i-1][0]+f[i-1][1]+f[i-1][2])%mod;
f[i][1]=(f[i-1][0]+f[i-1][1])%mod;
f[i][2]=(f[i-1][0]+f[i-1][2])%mod;
}
while(scanf("%d",&n) != EOF){
printf("%d\n",(f[n][0]+f[n][1]+f[n][2])%mod);
}
return 0;
}
P57. 核电站
设 \(f_{i,j}\) 为第 \(i\) 个坑前连续放了 \(j\) 个的满足条件的方案数。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
long long f[55][10];
int main(){
int n,m;
scanf("%d%d",&n,&m);
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
if(j==0){
for(int k=0;k<m;k++){
f[i][j]+=f[i-1][k];
}
}else{
f[i][j]=f[i-1][j-1];
}
}
}
long long ans=0;
for(int i=0;i<m;i++){
ans+=f[n][i];
}
printf("%lld\n",ans);
return 0;
}
传球游戏
设 \(f_{i,j}\) 为传 \(i\) 次到第 \(j\) 个人手中的方案数。
转移方程 \(f_{i,j}=f_{i-1,j-1}+f_{i-1,j+1}\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int f[35][35];
int main(){
int n,m;
scanf("%d%d",&n,&m);
f[0][1]=1;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
int l=j-1,r=j+1;
if(l<1)l=n;
if(r>n)r=1;
f[i][j]=f[i-1][l]+f[i-1][r];
}
}
printf("%d\n",f[m][1]); return 0;
}
P1990 覆盖墙壁
补一个洛谷上的,铺地毯升级版吧。
设 \(f[i][0]\) 为第 \(i\) 列没有空格的方案数, \(f[i][1]\) 为第 \(i\) 列只有一个空格的方案数。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int f[1000005][2];
int main(){
int n;
scanf("%d",&n);
f[1][0]=1,f[2][0]=2,f[2][1]=2;
for(int i=3;i<=n;i++){
f[i][0]=(f[i-1][1]+f[i-1][0]+f[i-2][0])%10000;
f[i][1]=(2*f[i-2][0]+f[i-1][1])%10000;
}
printf("%d\n",f[n][0]%10000);
return 0;
}
分治篇
快速幂
点击查看代码
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
long long qpow(long long x,long long y){
long long ans=1;
while(y){
if(y%2){
ans=ans*x%mod;
}
x=x*x%mod;
y/=2;
}
return ans;
}
int main(){
long long x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",qpow(x,y)%mod);
return 0;
}
P60. 烦恼的高考志愿
从小到大排序然后二分找到第一个比学生分数高的学校(\(res\)),答案就是 \(min(b[i]-a[res-1],a[res]-b[i])\) 。
有人 \(n,m\) 写反了调了半天。。。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[1000005],b[1000005];
int main(){
int m,n;
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
sort(a+1,a+m+1);
int ans=0;
for(int i=1;i<=n;i++){
int l=1,r=m,res;
while(l<=r){
int mid=(l+r)/2;
if(a[mid]>=b[i]){
res=mid;
r=mid-1;
}else{
l=mid+1;
}
}
if(b[i]<a[1]){
ans+=a[1]-b[i];
}else if(b[i]>a[m]){
ans+=b[i]-a[m];
}else{
ans+=min(abs(a[res]-b[i]),abs(a[res-1]-b[i]));
}
}
printf("%d\n",ans);
return 0;
}
P61. 工资计划
二分领到的最大工资,检验每次按最大工资领最少要几次。
哦还有题面的数据范围应该是 \(1≤N,M≤100000\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int n,m;
bool check(int x){
int sum=0,cnt=1;//cnt赋1因为最后一天一定要拿一次
for(int i=1;i<=n;i++){
sum+=a[i];
if(sum>x)cnt++,sum=a[i];
}
if(cnt<=m){
return true;
}else{
return false;
}
}
int main(){
scanf("%d%d",&n,&m);
int l=1,r=0,mid,res;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
l=max(a[i],l);
r+=a[i];
}
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
res=mid;
r=mid-1;
}else{
l=mid+1;
}
}
printf("%d\n",res);
return 0;
}
P3853 [TJOI2007] 路标设置
和上面那道一样的
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int r,n,k;
bool check(int x){
int cnt=0;
for(int i=2;i<=n;i++){
if(a[i]-a[i-1]>x){
cnt+=(a[i]-a[i-1])/x;
if((a[i]-a[i-1])%x==0)cnt-=1;
}
}
if(cnt>k){
return false;
}else{
return true;
}
}
int main(){
scanf("%d%d%d",&r,&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int l=1,mid,res;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
res=mid;
r=mid-1;
}else{
l=mid+1;
}
}
printf("%d\n",res);
return 0;
}
P8814 [CSP-J 2022] 解密
虽然应该直接推柿子,但是试着写了二分
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int k;
scanf("%d",&k);
while(k--){
int n,e,d;
scanf("%lld%lld%lld",&n,&d,&e);
int x=n-e*d+2;
int l=1,r=x/2,mid,res=0;
while(l<=r){
mid=(l+r)/2;
if(mid*(x-mid)>=n){
res=mid;
r=mid-1;
}else{
l=mid+1;
}
}
if(res*(x-res)!=n){
printf("NO\n");
continue;
}
printf("%lld %lld\n",res,x-res);
}
return 0;
}
贪心篇
P65. 删数问题
前面的数比后一位大就删掉
点击查看代码
#include <bits/stdc++.h>
using namespace std;
char c[245];
int main(){
scanf("%s",c+1);
int len=strlen(c+1),s;
scanf("%d",&s);
while(s--){
for(int i=1;i<=len;i++){
if(c[i]>c[i+1]){
for(int j=i;j<=len;j++){
c[j]=c[j+1];
}
len--;
break;
}
}
}
int t=1;
while(c[t]=='0'&&(t<len))t++;
if(t==len){
printf("0\n");
}else{
for(int i=t;i<=len;i++){
printf("%c",c[i]);
}
}
return 0;
}
P66. 最大整数
字典序大的放前面
点击查看代码
#include <bits/stdc++.h>
using namespace std;
string s[25];
bool cmp(string a,string b){
return a>b;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin >> s[i];
}
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++){
cout << s[i];
}
return 0;
}
P67. 合并果子
每次合并前两个最小的。
哦因为前面合并的会被反复加到后面合并的里面,所以先弄的越小越好。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct node{
long long x;
friend bool operator < (node a,node b){
return a.x>b.x;
}
};
priority_queue<node> Q;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
node a;
scanf("%d",&a.x);
Q.push(a);
}
long long ans=0;
while(Q.size()>1){
node a=Q.top();
Q.pop();
node b=Q.top();
Q.pop();
ans+=a.x+b.x;
node c;
c.x=a.x+b.x;
Q.push(c);
}
printf("%lld\n",ans);
return 0;
}
P68. 活动选择
越早结束的活动越好,给后面腾出了更多的时间。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct node{
int s,e;
}a[10005];
bool cmp(node a,node b){
return a.e<b.e;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].s,&a[i].e);
}
sort(a+1,a+n+1,cmp);
int ans=0,last=0;
for(int i=1;i<=n;i++){
if(a[i].s>=last){
ans++;
last=a[i].e;
}
}
printf("%d\n",ans);
return 0;
}
P72. 【USACO 2007 JAN】Protecting the Flowers
对于“找出一种最优排列顺序,使答案最优”的贪心题目,我们可以用“邻项交换”的方法去找出并证明贪心策略。
例如本题,我们假设有两头奶牛,其到达牛圈时间分别为 \(T_i\) ,\(T_{i+1}\) ,每分钟吃掉的花朵数分别为 \(D_i\) ,\(D_{i+1}\) 。
有两种情况:
① 排列顺序为 \(i~ i+1\) 则两头牛吃掉的花朵数为 \(res1=2T_iD_{i+1}\)
② 排列顺序为 \(i+1~ i\) 则两头牛吃掉的花朵数为 \(res2=2T_{i+1}D_i\)
假设前一种方案是最优解,则 \(res1<res2\) ,即 \(T_iD_{i+1}<T_{i+1}D_i\) 。
这样我们就找到了一种贪心的排序方法。
(以上是从别的题解上粘的)
其实可以对这个式子移项:\(D_i/T_i>D_{i+1}/T_{i+1}\) 。实际意义更好理解点,移动 \(i\) 奶牛时 \(D_i\) 不会对答案有贡献,所以先移单位时间内 \(D_i\) 大的。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct node{
int t,d;
}a[100005];
bool cmp(node a,node b){
return a.t*b.d<b.t*a.d;
}
int main(){
int n;
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].t,&a[i].d);
sum+=a[i].d;
}
sort(a+1,a+n+1,cmp);
long long ans=0;
for(int i=1;i<=n;i++){
ans+=2*a[i].t*(sum-a[i].d);
sum-=a[i].d;
}
printf("%lld\n",ans);
return 0;
}
P485. [NOIP2018 提高组] 铺设道路
\(a[i]>a[i-1]\) 时,填 \(a[i-1]\) 时会消掉一部分 \(a[i]\) ,剩下的 \(a[i]-a[i-1]\) 加到答案里。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main(){
int n;
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>a[i-1]){
ans+=a[i]-a[i-1];
}
}
printf("%d\n",ans);
return 0;
}
P505. 「一本通 1.1 例 5」智力大冲浪
消耗时间相同,所以把罚款高的任务先做了,并且在ddl最后做。
点击查看代码
#include <iostream>
#include <algorithm>
using namespace std;
struct node{
int time,money;
}a[520];
bool cmp(node a,node b)
{
return a.money>b.money;
}
bool visit[520];
int main()
{
int m,n;
cin >> m >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i].time;
}
for(int i=1;i<=n;i++)
{
cin >> a[i].money;
}
sort(a+1,a+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i].time;j>=1;j--)
{
if(!visit[j])
{
visit[j]=true;
a[i].money=0;
break;
}
}
}
for(int i=1;i<=n;i++)
{
ans+=a[i].money;
}
cout << m-ans <<endl;
return 0;
}
P9749 [CSP-J 2023] 公路
补一个有人考试没做出来的/ch
油箱无限大所以在便宜的地方加多多的,从左往右每次加到第一个比当前便宜的站点。还要处理一下剩油问题。
点击查看代码
//这个写的有点丑
#include <bits/stdc++.h>
using namespace std;
long long dis[100005],a[100005];
int main(){
int n,d;
scanf("%d%d",&n,&d);
for(int i=1;i<n;i++){
scanf("%d",&dis[i]);
dis[i]+=dis[i-1];
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
long long now=n,last=0,t=0;
long long ans=0;
for(int i=2;i<=n;i++){
if(a[i]<a[1]){
now=i;
break;
}
}
ans+=((dis[now-1]-1)/d+1)*a[1];
t=((dis[now-1]-1)/d+1)*d-dis[now-1];
//cout << now << " "<< last << endl;
last=now;
int flag=0;
while(now<n){
now++;
if(a[now]<a[last]){
flag=1;
//cout << now <<" " << last << endl;
if(dis[now-1]-dis[last-1]-t==0){
t=0;
}else if(dis[now-1]-dis[last-1]-t<0){
t-=dis[now-1]-dis[last-1];
}else{
ans+=((dis[now-1]-dis[last-1]-t-1)/d+1)*a[last];
//cout << t<< " "<<ans << endl;
t=(((dis[now-1]-dis[last-1]-t-1)/d+1)*d+t)-(dis[now-1]-dis[last-1]);
}
last=now;
}
}
if(last!=n){
ans+=((dis[n-1]-dis[last-1]-t-1)/d+1)*a[last];
}
printf("%lld\n",ans);
return 0;
}
dp篇
P86. 采药
每件物品只能放一次的01背包。
点击查看代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int w[101],v[101];
int f[101][1001];
int main()
{
int t,m;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&v[i],&w[i]);
}
for(int i=1;i<=m;i++)
{
for(int j=t;j>=0;j--)
{
if(j-v[i]>=0)
{
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}else{
f[i][j]=f[i-1][j];
}
}
}
printf("%d\n",f[m][t]);
return 0;
}
P87. Money Systems
设 \(f_i\) 表示凑齐 \(i\) 元的方案数。
转移 \(f_j=f_j+f_{j-v_i}\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int v[30];
long long f[10005];
int main(){
int V,n;
scanf("%d%d",&V,&n);
for(int i=1;i<=V;i++)scanf("%d",&v[i]);
f[0]=1;
for(int i=1;i<=V;i++)
for(int j=v[i];j<=n;j++)
f[j]+=f[j-v[i]];
printf("%lld\n",f[n]);
return 0;
}
P88.NASA的食物计划
有双重限制的01背包。
点击查看代码
#include <iostream>
using namespace std;
int f[401][401];
int v[51],m[51],h[51];
int main()
{
int V,M;
cin >> V >> M;
int n;
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> v[i] >> m[i] >> h[i] ;
}
for(int i=1;i<=n;i++)
{
for(int j=V;j>=v[i];j--)
{
for(int k=M;k>=m[i];k--)
{
f[j][k]=max(f[j][k],f[j-v[i]][k-m[i]]+h[i]);
}
}
}
cout << f[V][M] << endl;
return 0;
}
P89. 庆功会
每种物品有数量限制的多重背包,循环时中间加一层枚举物品数量。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=505,M=6005;
int v[N],w[N],s[N],f[M];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d%d%d",&v[i],&w[i],&s[i]);
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
for(int k=0;k<=j/v[i] && k<=s[i] ;k++)
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
printf("%d\n",f[m]);
return 0;
}
P90. 【USACO08JAN】跑步Running
设 \(f_{i,j}\) 为第 \(i\) 分钟疲惫度为 \(j\) 的最大距离。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=10005,M=505;
int f[N][M],d[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=m&&j<=i;j++){
f[i][0]=max(max(f[i][0],f[i-1][0]),f[i-j][j]);
f[i][j]=max(f[i-1][j-1]+d[i],f[i][j]);
}
}
printf("%d\n",f[n][0]);
return 0;
}
P140. 搭建双塔
设 \(f_{i,j}\) 为前 \(i\) 块水晶,两塔的高度差为 \(j\) 时的最大高度。
对于第 \(i\) 块,不放:\(f[i][j]=f[i-1][j]\) ;放矮塔上:\(f[i][j]=f[i-1][j+h[i]]\) ;放高塔上分类讨论,\(j>=h[i]\) 时:\(f[i][j]=f[i-1][j-h[i]]+h[i]\) ,\(j<h[i]\) 时,\(f[i][j]=f[i-1][h[i]-j]+j\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=105;
int h[N],f[N][2005];
int main(){
int n,maxh=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&h[i]),maxh+=h[i];
memset(f,-128,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=maxh;j>=0;j--){
f[i][j]=max(f[i-1][j],f[i][j]);
f[i][j]=max(f[i-1][j+h[i]],f[i][j]);
(j>=h[i])?f[i][j]=max(f[i-1][j-h[i]]+h[i],f[i][j]):f[i][j]=max(f[i-1][h[i]-j]+j,f[i][j]);
}
}
!f[n][0]?printf("Impossible\n"):printf("%d\n",f[n][0]);
return 0;
}
P141. 音量调节
近似成背包,设 \(f[i][j]\) 为前 \(i\) 首歌曲能否到达音量 \(j\)
点击查看代码
#include <iostream>
using namespace std;
int n,begin,maxlevel;
int ans;
int a[51];
int f[51][1001];
int main()
{
cin >> n >> begin >> maxlevel;
f[0][begin]=1;
for(int i=1;i<=n;i++)
{
cin >> a[i];
}
for(int i=1;i<=n;i++)
for(int j=maxlevel;j>=0;j--)
{
if(j-a[i]>=0)
{
f[i][j]=f[i][j]||f[i-1][j-a[i]];
}
if(j+a[i]<=maxlevel)
{
f[i][j]=f[i][j]||f[i-1][j+a[i]];
}
}
for(int i=maxlevel;i>=1;i--)
{
if(f[n][i]==1)
{
cout << i;
return 0;
}
}
cout << "-1";
return 0;
}
P138. 【NOIP 2008】传纸条
设 \(f[i][j][ii][jj]\) 为小渊传到小轩的纸条到达 \((i,j)\) ,从小轩传给小渊的纸条到达 \((ii,jj)\) 的路径上取得的最大的好心程度和。从下面传上去和从上面传下去是一样的。
P5662 [CSP-J2019] 纪念品
可以把它看做完全背包,当前金币数是容量,纪念品价格是价格,明天和今天价格差是价值,进行 \(t-1\) 次。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int T=105,N=105,M=1005;
int p[T][N],f[10005];
int main(){
int t,n,m;
scanf("%d%d%d",&t,&n,&m);
for(int i=1;i<=t;i++)
for(int j=1;j<=n;j++)
scanf("%d",&p[i][j]);
for(int i=1;i<t;i++){
memset(f,0,sizeof(f));
for(int k=1;k<=n;k++){
for(int j=p[i][k];j<=m;j++){
f[j]=max(f[j],f[j-p[i][k]]+p[i+1][k]-p[i][k]);
}
}
m+=f[m];
}
printf("%d\n",m);
return 0;
}
搜索篇
练几道搜索778(雾)
P5194 [USACO05DEC] Scales S
有几个剪枝,首先因为要找最大和并且输入数据不下降,所以从后往前先拿大的会快一些,其次当前缀和 \(sum_{now} + x<=c\) 时可以直接都拿走,不用再往下搜了。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
int n,c,a[N],sum[N],ans;
void dfs(int now,int x){
if(x>c)return;
if(sum[now]+x<=c){
ans=max(ans,sum[now]+x);
return;
}
ans=max(ans,x);
for(int i=now;i>=1;i--){
dfs(i-1,a[i]+x);
}
}
signed main(){
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
dfs(n,0);
printf("%lld\n",ans);
return 0;
}
P1378 油滴扩展
啊啊啊这个调了半天,就是要注意一下一个油的坐标在另一个有扩散后的范围内时,就不能扩散了,半径为 \(0\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=10;
pair <int,int> a[N];
int vis[N],n,x,y,xx,yy;
double ans,r[N];
double cal(int p){
r[p]=min(min(abs(a[p].first-x),abs(a[p].first-xx)),min(abs(a[p].second-y),abs(a[p].second-yy)));
for(int i=1;i<=n;i++){
if(vis[i]&&i!=p){
r[p]=min(r[p],max(0.0,sqrt((a[p].first-a[i].first)*(a[p].first-a[i].first)+(a[p].second-a[i].second)*(a[p].second-a[i].second))-abs(r[i])));
}
}
return r[p];
}
void dfs(int cnt,double sum){
if(cnt>n)return;
if(cnt==n){
ans=max(ans,sum);
return;
}
for(int i=1;i<=n;i++){
if(!vis[i]){
vis[i]=1;
dfs(cnt+1,sum+3.1415926*cal(i)*cal(i));
vis[i]=0,r[i]=0;
}
}
}
int main(){
scanf("%d",&n);
scanf("%d%d%d%d",&x,&y,&xx,&yy);
int s=abs(x-xx)*abs(y-yy);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].first,&a[i].second);
dfs(0,0);
printf("%.lf\n",s-ans);
return 0;
}
P3958 [NOIP2017 提高组] 奶酪
long long 没开全又调了半天。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
long long n,h,r,vis[N];
struct node{
long long x,y,z;
}a[N];
bool flag;
bool judge(int i,int j){
long long dis=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z);
return dis<=4*r*r;
}
void dfs(int p){
if(flag)return;
if(a[p].z+r>=h){
flag=1;
return;
}
for(int i=1;i<=n;i++){
if(i!=p&&judge(p,i)&&!vis[i]){
vis[i]=1;
dfs(i);
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
flag=0;
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(0));
scanf("%lld%lld%lld",&n,&h,&r);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
}
for(int i=1;i<=n;i++){
if(a[i].z<=r){
vis[i]=1;
dfs(i);
}
}
flag?printf("Yes\n"):printf("No\n");
}
return 0;
}
P1032 [NOIP2002 提高组] 字串变换
啊这个叫做双向搜索/yiw 就是从起始状态和末状态同时搜,然后 \(meet~ in~ the~ middle\)
和题解学会了substring 的用法 s.substr(起始位置,末端位置(不包含))
但是为什么一发就过了/xia
点击查看代码
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
string s1[10],s2[10],a,b;
queue <pair<string,int> > q1,q2;
map <string,int> m1,m2;
int n=1;
void bfs(){
q1.push({a,0});
q2.push({b,0});
while(!q1.empty()&&!q2.empty()){
string now=q1.front().fi;
int step=q1.front().se;
q1.pop();
if(step>5){
printf("NO ANSWER!\n");
return;
}
for(int i=1;i<=n;i++){
if(now.size()<s1[i].size())continue;
for(int j=0;j<=now.size()-s1[i].size();j++){
if(now.substr(j,s1[i].size())!=s1[i])continue;
string ss=now.substr(0,j);
ss+=s2[i];
ss+=now.substr(j+s1[i].size());
if(m1[ss])continue;
if(m2[ss]){
printf("%d\n",m2[ss]+step+1);
return;
}
m1[ss]=step+1;
q1.push({ss,step+1});
}
}
//qwq
string now2=q2.front().fi;
int step2=q2.front().se;
q2.pop();
if(step2>5){
printf("NO ANSWER!\n");
return;
}
for(int i=1;i<=n;i++){
if(now2.size()<s2[i].size())continue;
for(int j=0;j<=now2.size()-s2[i].size();j++){
if(now2.substr(j,s2[i].size())!=s2[i])continue;
string s=now2.substr(0,j);
s+=s1[i];
s+=now2.substr(j+s2[i].size());
if(m2[s])continue;
if(m1[s]){
printf("%d\n",m1[s]+step2+1);
return;
}
m2[s]=step2+1;
q2.push({s,step2+1});
}
}
}
printf("NO ANSWER!\n");
}
int main(){
cin >> a >> b;
while(cin >> s1[n] >>s2[n])n++;
bfs();
return 0;
}