11.23

11.23

t1 chess——记搜dp

状态设错了,就靠着(0)骗分了

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=155;
const int P=1e9+7;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,m,lim;
int f[N][N][25][25]; 
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
inline int work(int a,int b,int c,int d) {
	if(a==n&&b==m) return 1;
	if(f[a][b][c][d]!=-1) return f[a][b][c][d];
	int tmp=0;
	if(a<n&&c<lim) add(tmp,work(a+1,b,c+1,max(0,d-1)));
	if(b<m&&d<lim) add(tmp,work(a,b+1,max(0,c-1),d+1));
	return f[a][b][c][d]=tmp;
}
int main() {
	n=read();m=read();lim=read();
    for(int i=0;i<=n;i++)
       for(int j=0;j<=m;j++)
          for(int x=0;x<=lim;x++)
             for(int y=0;y<=lim;y++)
             	f[i][j][x][y]=-1;
	printf("%d
",work(0,0,0,0));
	return 0;
}

t2 taxi ——记搜

20分暴力写挂呜呜呜 ——啊是读题出锅了,一定要严格按照编号大小上车(我以为这个限制只是对于在同一停靠站的)

???一套题三道搜索

我们发现我们并不需要记录编号,我们并不关心出租车里是谁,我们只关心车里的人要到哪里
去,所以只需要记录要到达的停靠点编号,由于4人满后必须要送一个人到目的地,所以我们只需要记录3个人的目的地即可,这样会出现较多的重复状态,可以用记忆化搜索减少重复状态的搜索。 时间复杂度
(O(nk^4))

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=2005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,k,u[N],v[N];
int f[2005][11][11][11][11]; 
//当前编号,当前位置,三个目的地位置
inline int dis(int x){return x>0?x:(-x);}
inline void Min(int &x,int y){if(x>y)x=y;}
int dfs(int i,int pos,int a,int b,int c) {
	if(~f[i][pos][a][b][c]) return f[i][pos][a][b][c];
	int res=0x3f3f3f3f;
	// 如果出租车不为空
	if(a) Min(res,dfs(i,a,0,b,c)+dis(a-pos)+1);
	if(b) Min(res,dfs(i,b,a,0,c)+dis(b-pos)+1);
	if(c) Min(res,dfs(i,c,a,b,0)+dis(c-pos)+1);
	if(i>n) {
		if(a==0&&b==0&&c==0) return 0;
		return f[i][pos][a][b][c]=res;
	}
	// 如果此时出租车坐满了,先下去一个人
	if(a && b && c) {
		Min(res,dfs(i+1,v[i],a,b,c)+dis(u[i]-pos)+dis(u[i]-v[i])+2);
		Min(res,dfs(i+1,a,v[i],b,c)+dis(u[i]-pos)+dis(u[i]-a)+2);
		Min(res,dfs(i+1,b,a,v[i],c)+dis(u[i]-pos)+dis(u[i]-b)+2);
		Min(res,dfs(i+1,c,a,b,v[i])+dis(u[i]-pos)+dis(u[i]-c)+2);
	}
	else {
		if(!a) Min(res,dfs(i+1,u[i],v[i],b,c)+dis(pos-u[i])+1);
		else if(!b) Min(res,dfs(i+1,u[i],a,v[i],c)+dis(pos-u[i])+1);
		else Min(res,dfs(i+1,u[i],a,b,v[i])+dis(pos-u[i])+1);
	}
	return f[i][pos][a][b][c]=res;
}
int main() {
	memset(f,0xff,sizeof(f));//赋值为-1
	n=read();k=read();
	for(int i=1;i<=n;i++) u[i]=read(),v[i]=read();
		printf("%d
",dfs(1,1,0,0,0));;
	return 0;
}

t3 stone 折半爆搜

数据范围显然了,搜出前一半,然后搜后一半。就到这我傻了...我以为要存下来两个数组一个一个对应加再(\%m)找最大值,那这复杂度不又回到 (O(2^35))了???不管了想不透打暴力

考完被(wkp)一语惊醒,加到超过(m)的后再(\%m)肯定不优了——肯定还不如自己大

所以只要(*--upperbound(m-1-sum))即可

还是太傻了

#include <set>
#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=45;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,m,a[N];
set<int>s;
int ans;
void dfs(int x,int sum) {
	if(x==n/2+1) {
		s.insert(sum);
		return;
	}
	dfs(x+1,sum);
	dfs(x+1,(sum+a[x])%m);
}
void dfs2(int x,int sum) {
	if(x==n+1) {
		set<int>::iterator it=s.upper_bound(m-sum-1);
		Max(ans,*(--it)+sum);
		return;
	}
	dfs2(x+1,sum);
	dfs2(x+1,(sum+a[x])%m);
}
int main() {
	n=read();m=read();
	bool flag=1;
	for(int i=1;i<=n;i++) {
		a[i]=read()%m;		
		if(a[i]) flag=0;
	}
	if(flag) {
		puts("0");return 0;
	}
	s.insert(0);	
	dfs(1,0);
	dfs2(n/2+1,0);
	printf("%d
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/ke-xin/p/14024478.html