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;
}