做题小记·基础算法

重生之我是普及组选手
题目大多来源于 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;
}
posted @ 2024-06-27 20:20  Bao111  阅读(7)  评论(0编辑  收藏  举报