搜索,折半搜索训练记录 2025.1
「NOIP2009」靶形数独
https://www.gxyzoj.com/d/hzoj/p/P437
暴力枚举每一个空位的方法显然会T,可以发现,其实每一位的方案数有限,所以可以从可能性最少的点开始枚举
此时,填入一个数就会给后面的点增加限制,从而提高效率
因此,按照可选的情况数预处理填数队列即可,注意,选出点(x,y)后,要假设这个点已经填了
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int a[10][10],cnt,cntx[10],cnty[10],cnta[10],id[10][10];
struct node{
int x,y;
}s[100];
bool vis[10][10];
int dx[10]={0,1,1,1,4,4,4,7,7,7};
int dy[10]={0,1,4,7,1,4,7,1,4,7};
int b[10][10],ans=-1;
int c[9][9]={
{6,6,6,6,6,6,6,6,6},
{6,7,7,7,7,7,7,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,9,10,9,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,7,7,7,7,7,7,6},
{6,6,6,6,6,6,6,6,6},
};
int clac()
{
int res=0;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
res+=b[i][j]*c[i-1][j-1];
// printf("%d ",b[i][j]);
}
// printf("\n");
}
// printf("%d\n",res);
return res;
}
bool check(int x,int y,int val)
{
// for(int i=1;i<=9;i++)
// {
// for(int j=1;j<=9;j++)
// {
// printf("%d ",b[i][j]);
// }
// printf("\n");
// }
for(int i=1;i<=9;i++)
{
if(i!=y&&b[x][i]==val) return 0;
}
for(int i=1;i<=9;i++)
{
if(i!=x&&b[i][y]==val) return 0;
}
for(int i=dx[id[x][y]];i<dx[id[x][y]]+3;i++)
{
for(int j=dy[id[x][y]];j<dy[id[x][y]]+3;j++)
{
if((i!=x||j!=y)&&b[i][j]==val) return 0;
}
}
return 1;
}
void dfs(int now)
{
if(now==cnt+1)
{
ans=max(ans,clac());
return;
}
int x=s[now].x,y=s[now].y;
// printf("%d %d %d %d\n",now,x,y,b[s[now-1].x][s[now-1].y]);
for(int i=1;i<=9;i++)
{
if(check(x,y,i))
{
b[x][y]=i;
dfs(now+1);
b[x][y]=0;
}
}
}
int main()
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
scanf("%d",&a[i][j]);
int x=(i-1)/3,y=(j+2)/3;
id[i][j]=x*3+y;
b[i][j]=a[i][j];
if(!a[i][j]) cnt++;
else cntx[i]++,cnty[j]++,cnta[id[i][j]]++;
}
}
for(int k=1;k<=cnt;k++)
{
int mx=0;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(!vis[i][j]&&!a[i][j])
{
if(mx<cntx[i]+cnty[j]+cnta[id[i][j]])
{
mx=cntx[i]+cnty[j]+cnta[id[i][j]];
s[k].x=i,s[k].y=j;
}
}
}
}
cntx[s[k].x]++,cnty[s[k].y]++;
cnta[id[s[k].x][s[k].y]]++;
vis[s[k].x][s[k].y]=1;
// printf("%d %d\n",s[k].x,s[k].y);
}
// printf("%d\n",cnt);
dfs(1);
printf("%d",ans);
return 0;
}
[CQOI2013] 新数独
https://www.gxyzoj.com/d/hzoj/p/4367
离谱做法,数独直接搜共约
先搜索每一宫的所有可能性,因为限制很多,所以情况很少,储存到vector中
然后将他们拼接起来,判断是否满足条件即可
代码:
点击查看代码
#include<cstdio>
#include<string>
#include<iostream>
#include<vector>
using namespace std;
int row[10][10],lin[10][10],cnt1,cnt2;
int a[5][5],b[5][5],c[10][10],d[10],cnt[10];
struct node{
int a[4][4];
};
vector<node> v[10];
bool check()
{
for(int i=1;i<=9;i++)
{
int x=(i-1)/3+1,y=(i-1)%3+1;
c[x][y]=d[i];
}
for(int i=1;i<=3;i++)
{
for(int j=1;j<=2;j++)
{
if(c[i][j]>c[i][j+1]&&a[i][j]) return 0;
if(c[i][j]<c[i][j+1]&&!a[i][j]) return 0;
}
}
for(int i=1;i<=2;i++)
{
for(int j=1;j<=3;j++)
{
if(c[i][j]>c[i+1][j]&&!b[i][j]) return 0;
if(c[i][j]<c[i+1][j]&&b[i][j]) return 0;
}
}
return 1;
}
bool vis[10];
void dfs(int now,int id)
{
if(now==10)
{
if(check())
{
node tmp;
for(int i=1;i<=9;i++)
{
int x=(i-1)/3+1,y=(i-1)%3+1;
tmp.a[x][y]=d[i];
}
cnt[id]++;
v[id].push_back(tmp);
}
return;
}
for(int i=1;i<=9;i++)
{
if(!vis[i])
{
vis[i]=1,d[now]=i;
dfs(now+1,id);
vis[i]=0,d[now]=0;
}
}
}
int dx[10]={0,1,1,1,4,4,4,7,7,7};
int dy[10]={0,1,4,7,1,4,7,1,4,7};
bool check1(int x,int y,int val)
{
for(int i=1;i<=9;i++)
{
if(i!=y&&c[x][i]==val) return 0;
}
for(int i=1;i<=9;i++)
{
if(i!=x&&c[i][y]==val) return 0;
}
return 1;
}
bool dfs1(int now)
{
if(now==10)
{
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
printf("%d ",c[i][j]);
}
printf("\n");
}
return 1;
}
bool fl=0,fl1;
for(int i=0;i<cnt[now];i++)
{
for(int j=1;j<=3;j++)
{
for(int k=1;k<=3;k++)
{
fl1=check1(j+dx[now]-1,k+dy[now]-1,v[now][i].a[j][k]);
if(!fl1) break;
}
if(!fl1) break;
}
if(fl1)
{
for(int j=1;j<=3;j++)
{
for(int k=1;k<=3;k++)
{
c[j+dx[now]-1][k+dy[now]-1]=v[now][i].a[j][k];
}
}
if(dfs1(now+1))
{
fl=1;
break;
}
}
for(int j=1;j<=3;j++)
{
for(int k=1;k<=3;k++)
{
c[j+dx[now]-1][k+dy[now]-1]=0;
}
}
}
return fl;
}
int pd[20]={0,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1};
int main()
{
for(int i=1;i<=15;i++)
{
string s;
getline(cin,s);
int fl=0;
if(pd[i])
{
cnt1++;
for(int j=1,k=0;j<=6,k<s.size();k++)
{
if(s[k]=='<') row[cnt1][j]=1,j++;
if(s[k]=='>') j++;
}
}
else
{
cnt2++;
for(int j=1,k=0;j<=9,k<s.size();k++)
{
if(s[k]=='v') lin[cnt2][j]=1,j++;
if(s[k]=='^') j++;
}
}
}
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
c[i][j]=0;
}
}
for(int i=1;i<=9;i++)
{
int x=(i-1)/3,y=(i-1)%3+1;
int tx=3*x+1,ty=2*y-1;
for(int j=tx;j<tx+3;j++)
{
for(int k=ty;k<ty+2;k++)
{
a[j-tx+1][k-ty+1]=row[j][k];
}
}
tx=2*x+1,ty=3*y-2;
for(int j=tx;j<tx+2;j++)
{
for(int k=ty;k<ty+3;k++)
{
b[j-tx+1][k-ty+1]=lin[j][k];
}
}
dfs(1,i);
}
bool fl=dfs1(1);
return 0;
}
Anya and Cubes
https://www.gxyzoj.com/d/hzoj/p/CF525E
折半搜索+抽象卡常
每个数有三种情况:原数,阶乘或不选,
将和丢进map中,按阶乘的个数先分一下类,统计时加上所有
注意,直接加会T,统计之前加一个判断if(mp[i].count(m-sum)),不判断会增加很多无用信息
点击查看代码
#include<cstdio>
#include<map>
#define ll long long
using namespace std;
int n,k,a[30];
ll fac[25],m;
map<ll,int> mp[30];
int get(int x)
{
int res=0;
while(x)
{
if(x%3==2) res++;
x/=3;
}
return res;
}
int qpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1) res*=x;
x*=x;
y>>=1;
}
return res;
}
int main()
{
scanf("%d%d%lld",&n,&k,&m);
fac[0]=1;
for(int i=1;i<20;i++)
{
fac[i]=fac[i-1]*i;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int tmp=n/2;
int tmp1=qpow(3,tmp),tmp2=qpow(3,n-tmp);
for(int s=0;s<tmp1;s++)
{
ll sum=0;
int st=s;
for(int i=1;i<=tmp;i++)
{
int x=st%3;
if(x==1) sum+=a[i];
if(x==2)
{
if(a[i]<19) sum+=fac[a[i]];
else
{
sum=-1;
break;
}
}
st/=3;
}
// printf("%d %d\n",s,sum);
if(sum!=-1&&sum<=m)
mp[get(s)][sum]++;
}
ll ans=0;
for(int s=0;s<tmp2;s++)
{
ll sum=0;
int st=s;
for(int i=tmp+1;i<=n;i++)
{
int x=st%3;
if(x==1) sum+=a[i];
if(x==2)
{
if(a[i]<19) sum+=fac[a[i]];
else
{
sum=-1;
break;
}
}
st/=3;
}
if(sum!=-1&&sum<=m)
{
int x=k-get(s);
for(int i=0;i<=x;i++)
{
if(mp[i].count(m-sum))
{
ans+=mp[i][m-sum];
}
}
}
}
printf("%lld",ans);
return 0;
}
Two Fairs
https://www.gxyzoj.com/d/hzoj/p/4363
因为要所有路径都经过a,b,那么从b出发,不经过a就走不到其中一个点,从a出发同理
所以从a开始bfs,且不将b入队,统计除b以外未经过的点数,b同理,不经a
最后两者相乘就是答案,注意long long
点击查看代码
#include<cstdio>
#include<queue>
using namespace std;
int n,m,T,a,b,head[200005],edgenum;
struct edge{
int to,nxt;
}e[1000005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
int vis[200005];
queue<int> q;
int bfs(int s,int t)
{
for(int i=1;i<=n;i++) vis[i]=0;
while(!q.empty()) q.pop();
q.push(s);
vis[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(vis[v]||v==t) continue;
vis[v]=1;
q.push(v);
}
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&i!=t) cnt++;
}
return cnt;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&a,&b);
for(int i=1;i<=n;i++) head[i]=0;
edgenum=0;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
printf("%lld\n",1ll*bfs(a,b)*bfs(b,a));
}
return 0;
}
Expression
https://www.gxyzoj.com/d/hzoj/p/4364
很有意思的一道题
因为每个数至多会加6位,所以肯定不能暴力搜索
可以考虑每一位的贡献,记
则当
否则分三种情况,即在a,b或c种加一位,使其满足条件
注意,当只有进位的时候,在c中加一个1即可,否则死循环
点击查看代码
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
string s;
int ta[30],tb[30],tc[30],minlen=1e9,ansa[30],ansb[30],ansc[30];
void dfs(int now,int a,int b,int c,int jw)
{
// printf("%d %d %d %d %d\n",now,a,b,c,jw);
if(!a&&!b&&!c&&!jw)
{
// printf("1");
int cnt=0;
for(int i=29;i>0;i--)
{
if(ta[i]!=0)
{
cnt+=i;
break;
}
}
for(int i=29;i>0;i--)
{
if(tb[i]!=0)
{
cnt+=i;
break;
}
}
for(int i=29;i>0;i--)
{
if(tc[i]!=0)
{
cnt+=i;
break;
}
}
// printf("%d %d\n",cnt,minlen);
if(cnt<minlen)
{
minlen=cnt;
for(int i=1;i<30;i++)
{
ansa[i]=ta[i],ansb[i]=tb[i],ansc[i]=tc[i];
}
}
return;
}
int da=a%10,db=b%10,dc=c%10;
if(!a&&!b&&!c&&jw)
{
tc[now]=jw;
dfs(now+1,0,0,0,0);
}
else if((jw+da+db-dc)%10==0)
{
ta[now]=da,tb[now]=db,tc[now]=dc;
dfs(now+1,a/10,b/10,c/10,(jw+da+db-dc)/10);
}
else
{
if(a||b)
{
ta[now]=da,tb[now]=db,tc[now]=(jw+da+db)%10;
dfs(now+1,a/10,b/10,c,(jw+da+db)/10);
}
if(dc>=db+jw&&(c||b))
{
ta[now]=dc-db-jw,tb[now]=db,tc[now]=dc;
dfs(now+1,a,b/10,c/10,0);
}
else if(c||b)
{
ta[now]=dc+10-db-jw,tb[now]=db,tc[now]=dc;
dfs(now+1,a,b/10,c/10,1);
}
if(dc>=da+jw&&(c||a))
{
ta[now]=da,tb[now]=dc-da-jw,tc[now]=dc;
dfs(now+1,a/10,b,c/10,0);
}
else if(c||a)
{
ta[now]=da,tb[now]=dc-da-jw+10,tc[now]=dc;
dfs(now+1,a/10,b,c/10,1);
}
}
ta[now]=tb[now]=tc[now]=0;
}
int a,b,c;
int main()
{
cin>>s;
int fl=0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='+') fl=1;
else if(s[i]=='=') fl=2;
else
{
if(fl==0) a=a*10+s[i]-'0';
if(fl==1) b=b*10+s[i]-'0';
if(fl==2) c=c*10+s[i]-'0';
}
}
dfs(1,a,b,c,0);
fl=0;
for(int i=29;i>0;i--)
{
if(ansa[i]!=0||fl)
{
printf("%d",ansa[i]);
fl=1;
}
}
fl=0;
printf("+");
for(int i=29;i>0;i--)
{
if(ansb[i]!=0||fl)
{
printf("%d",ansb[i]);
fl=1;
}
}
printf("=");
fl=0;
for(int i=29;i>0;i--)
{
if(ansc[i]!=0||fl)
{
printf("%d",ansc[i]);
fl=1;
}
}
return 0;
}
Distinct Paths
https://www.gxyzoj.com/d/hzoj/p/CF293B
可以发现,当
但是这样暴力依然会T,考虑剪枝:
-
如果当前到终点的距离小于可支配的颜色数,直接无解
-
如果一些转台时等价的,可以只搜其中一个
关于什么时等价状态,记
可以先统计固定的颜色的数量,然后在dfs过程中,如果当前点有k个
点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,m,k,f[15][15],g[1025],a[15][15];
int cnt[20];
int get(int x)
{
int res=0;
while(x)
{
if(x&1) res++;
x>>=1;
}
return res;
}
ll dfs(int now)
{
if(now==n*m+1)
{
return 1;
}
int x=(now-1)/m+1,y=(now-1)%m+1;
// printf("%d %d %d\n",a[x][y],x,y);
int s=f[x][y-1]|f[x-1][y];
// printf("%d %d\n",now,s);
int st=((1<<k)-1)^s;
ll res=0,tmp=-1;
if(g[st]+x+y<=n+m) return 0;
// printf("%d ",now);
for(int i=1;i<=k;i++)
{
if(s&(1<<(i-1))) continue;
if(!a[x][y]||a[x][y]==i)
{
// printf("%d %d\n",now,i);
f[x][y]=s|(1<<(i-1));
cnt[i]++;
if(cnt[i]==1)
{
if(tmp==-1)
{
tmp=dfs(now+1);
}
res+=tmp;
}
else res+=dfs(now+1);
res%=mod;
cnt[i]--;
}
}
// printf("%d %d\n",now,res);
return res;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
if(n+m-1>k)
{
printf("0");
return 0;
}
for(int i=1;i<(1<<k);i++)
{
g[i]=get(i);
// printf("%d ",g[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
cnt[a[i][j]]++;
}
}
ll ans=dfs(1);
printf("%d",ans);
return 0;
}
Bear and Square Grid
https://www.gxyzoj.com/d/hzoj/p/4366
显然,如果去掉一个方阵,那么与其中的X相邻的联通块就会合并
所以最简单的,枚举每个方框的位置,然后暴力看有多少X并将与这些X相邻的联通块的大小相加
但是这样并不优,因为每次的枚举和重复运算很多
可以先计算开始行在同一行的第一个,然后直接在这个基础上操作
对于每个联通块,统计当前范围共有多少面与他相邻,记为
...
.X.
假设此时在(2,2),则应+3
如果在添加点a前,联通块b的相邻数
同理,减去后为0,则
每一次加上第i列,减去第i-m列即可,记得还要加上X的个数
ans就是所有sum的最大值,预处理所有联通块即可
点击查看代码
#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,a[505][505],id[505][505],tot,siz[250004];
struct node{
int x,y;
};
queue<node> q;
int dx[4]={-1,0,0,1};
int dy[4]={0,-1,1,0};
void bfs(int x,int y)
{
tot++;
q.push((node){x,y});
id[x][y]=tot;
int sum=1;
while(!q.empty())
{
node u=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=u.x+dx[i],ty=u.y+dy[i];
if(!tx||!ty||tx>n||ty>n) continue;
if(a[tx][ty]||id[tx][ty]) continue;
id[tx][ty]=tot,sum++;
q.push((node){tx,ty});
}
}
siz[tot]=sum;
}
int cnt[250005];
int main()
{
scanf("%d%d",&n,&m);
bool fl=0;
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
for(int j=1;j<=n;j++)
{
if(s[j-1]=='X') a[i][j]=1,fl=1;
}
}
if(!fl)
{
printf("%d",n*n);
return 0;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(!a[i][j]&&!id[i][j])
{
bfs(i,j);
// printf("%d %d %d\n",i,j,siz[tot]);
}
// printf("%d ",id[i][j]);
}
// printf("\n");
}
int ans=0;
for(int i=1;i<=n-m+1;i++)
{
for(int j=1;j<=tot;j++) cnt[j]=0;
int sum=0;
for(int j=i;j<=i+m-1;j++)
{
for(int k=1;k<=m;k++)
{
if(!a[j][k]) continue;
sum++;
for(int t=0;t<4;t++)
{
int tx=j+dx[t],ty=k+dy[t];
if(!tx||!ty||tx>n||ty>n) continue;
if(id[tx][ty])
{
cnt[id[tx][ty]]++;
if(cnt[id[tx][ty]]==1)
{
sum+=siz[id[tx][ty]];
}
}
}
}
}
ans=max(ans,sum);
// printf("%d %d\n",i,sum);
for(int k=1+m;k<=n;k++)
{
for(int j=i;j<=i+m-1;j++)
{
if(!a[j][k-m]) continue;
sum--;
for(int t=0;t<4;t++)
{
int tx=j+dx[t],ty=k-m+dy[t];
if(!tx||!ty||tx>n||ty>n) continue;
if(id[tx][ty])
{
// if(i==3)
// printf("%d %d %d\n",k,tx,ty);
cnt[id[tx][ty]]--;
if(!cnt[id[tx][ty]])
sum-=siz[id[tx][ty]];
}
}
}
for(int j=i;j<=i+m-1;j++)
{
if(!a[j][k]) continue;
sum++;
for(int t=0;t<4;t++)
{
int tx=j+dx[t],ty=k+dy[t];
if(!tx||!ty||tx>n||ty>n) continue;
if(id[tx][ty])
{
cnt[id[tx][ty]]++;
if(cnt[id[tx][ty]]==1)
sum+=siz[id[tx][ty]];
}
}
}
ans=max(ans,sum);
// printf("%d %d %d\n",i,k,sum);
}
}
printf("%d",ans);
return 0;
}
[SDOI2011] 迷宫探险
https://www.gxyzoj.com/d/hzoj/p/P783
抽象题
先考虑要记录什么信息,首先是位置和血量,因为每个陷阱的状态走过后是已知,因此还要统计所有陷阱是未知/有害/无害
因为是按照最优策略,考虑dp
但是根据题意,可以向4个方向走,那么有可能成环,为保证状态的单向改变,每次尽量找有害或未知的格子走
而当当前格子可以不经过任何有害或未知格子到达一个终点时,直接返回1
接下来看有害或未知格子如何搜
如果要走有害的格子,则血量必须大于1,此时走它必然扣血
如果是未知格子,如果血量大于1,则有害无害都能走,所以按照概率计算即可
如果等于1,就只能走无害,此时,只能用这个点是无害的概率与对应结果的概率之积
而对于状态为s,i无害的概率,可以预处理完成
注意,因为输入的只有当前一定有害或无害,所以要递归出部分未知时的概率
点击查看代码
#include<cstdio>
#include<string>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,k,h,a[35][35],sx,sy,p1[10],s[40];
int p[500],st1;
bool pd(int x)
{
int res=0;
while(x)
{
if(!(x%3)) return 0;
x/=3,res++;
}
if(res<k) return 0;
return 1;
}
int get(int x,int y)
{
if(p[x]>=0) return p[x];
for(int i=y;;i++)
{
if((x/p1[i])%3) continue;
p[x]=get(x+p1[i],i+1)+get(x+p1[i]*2,i+1);
return p[x];
}
}
double f[35][35][250][6],p2[35][250];
bool vis[35][35][250][6],v1[35][35][250];
struct node{
int x,y;
};
vector<node> v[35][35][250];
int dx[4]={-1,0,0,1};
int dy[4]={0,-1,1,0};
int tag[35][35],tot;
inline void dfs2(int x,int y)
{
for(int i=0;i<4;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(tx&&ty&&tx<=n&&ty<=m&&tag[tx][ty]!=tot)
{
tag[tx][ty]=tot;
if(a[tx][ty]==-2)
{
dfs2(tx,ty);
}
else if(a[tx][ty]==-3)
{
for(int j=1;j<=h;j++)
{
vis[x][y][st1][j]=vis[sx][sy][st1][j]=1;
f[x][y][st1][j]=f[sx][sy][st1][j]=1;
}
return;
}
else
{
if(a[tx][ty]>=0&&a[tx][ty]<k)
{
if((st1/p1[a[tx][ty]])%3==1)
{
dfs2(tx,ty);
}
else
{
v[sx][sy][st1].push_back((node){tx,ty});
}
}
}
}
if(vis[sx][sy][st1][1])
{
for(int j=1;j<=h;j++)
{
vis[x][y][st1][j]=1;
f[x][y][st1][j]=1;
}
return;
}
}
}
inline double dfs(int x,int y,int st,int h1)
{
if(vis[x][y][st][h1])
{
return f[x][y][st][h1];
}
// printf("%d %d %d %d\n",x,y,st,h1);
if(!v1[x][y][st])
{
v1[x][y][st]=1;
sx=x,sy=y,st1=st;
tag[x][y]=++tot;
dfs2(x,y);
if(vis[x][y][st][h1]) return 1;
}
vis[x][y][st][h1]=1;
double res=0;
for(int i=0;i<v[x][y][st].size();i++)
{
int tx=v[x][y][st][i].x,ty=v[x][y][st][i].y;
int col=a[tx][ty];
if((st/p1[col])%3==2)
{
if(h1>1)
{
res=max(res,dfs(tx,ty,st,h1-1));
}
}
else
{
if(h1==1)
{
res=max(res,dfs(tx,ty,st+p1[col],h1)*p2[st][col]);
}
else
{
res=max(res,dfs(tx,ty,st+p1[col],h1)*p2[st][col]+dfs(tx,ty,st+p1[col]*2,h1-1)*(1-p2[st][col]));
}
}
if(res==1)
{
f[x][y][st][h1]=1;
return 1;
}
}
return f[x][y][st][h1]=res;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&h);
p1[0]=1;
for(int i=1;i<=k;i++)
{
p1[i]=p1[i-1]*3;
}
for(int i=1;i<=n;i++)
{
string s1;
cin>>s1;
for(int j=1;j<=m;j++)
{
if(s1[j-1]=='#') a[i][j]=-1;
else if(s1[j-1]=='.') a[i][j]=-2;
else if(s1[j-1]=='@') a[i][j]=-3;
else if(s1[j-1]=='$')
{
a[i][j]=-2,sx=i,sy=j;
}
else a[i][j]=s1[j-1]-'A';
}
}
int cnts=0;
for(int i=0;i<p1[k];i++)
{
p[i]=-1;
if(pd(i)) s[++cnts]=i;
}
// printf("%d ",cnts);
for(int i=1;i<=cnts;i++)
{
scanf("%d",&p[s[i]]);
}
for(int i=0;i<p1[k];i+=3)
{
int tmp=get(i,0);
}
for(int i=0;i<p1[k];i++)
{
for(int j=0;j<k;j++)
{
if(!((i/p1[j])%3))
p2[i][j]=p[i+p1[j]]*1.0/((p[i+p1[j]]+p[i+p1[j]*2])*1.0);
}
}
// for(int i=0;i<p1[k];i++)
// {
// printf("%d ",p[i]);
// }
// printf("1");
printf("%.3lf",dfs(sx,sy,0,h));
return 0;
}
Lizard Era: Beginning
https://www.gxyzoj.com/d/hzoj/p/CF585D
因为每一位三种状态,25位,共
显然折半搜索,0表示LM,1表示LW,2表示MW
假设前半段
由等量代换,维护两组差分,就能保证相等
因为所有输入的绝对值
所以可以将差分值压缩成一个long long然后扔进map即可
注意,在前半段往map扔时,若压缩值相等,选L大的
点击查看代码
#include<cstdio>
#include<map>
#define ll long long
using namespace std;
int n,a[30],b[30],c[30],fl,x,tmp1,tmp2;
ll p=1e9;
struct node{
int st,val;
};
map<ll,node> mp;
int qpow(int x,int y)
{
int res=1;
while(y)
{
if(y&1) res=res*x;
x*=x;
y>>=1;
}
return res;
}
void print(int s1,int s2)
{
fl=1;
for(int i=1;i<=x;i++)
{
if(s1%3==0) printf("LM\n");
if(s1%3==1) printf("LW\n");
if(s1%3==2) printf("MW\n");
s1/=3;
}
for(int i=x+1;i<=n;i++)
{
if(s2%3==0) printf("LM\n");
if(s2%3==1) printf("LW\n");
if(s2%3==2) printf("MW\n");
s2/=3;
}
}
int mx=-1e9,s1,s2;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i],&b[i],&c[i]);
}
x=n/2;
tmp1=qpow(3,x),tmp2=qpow(3,n-x);
for(int s=0;s<tmp1;s++)
{
int l=0,m=0,w=0,st=s;
for(int i=1;i<=x;i++)
{
if(st%3==0) l+=a[i],m+=b[i];
if(st%3==1) l+=a[i],w+=c[i];
if(st%3==2) m+=b[i],w+=c[i];
st/=3;
}
ll sta=(l-m)*p+(l-w);
// printf("%d %d %d %d %lld\n",s,l,m,w,sta);
if((mp.count(sta)&&mp[sta].val<l)||!mp.count(sta))
{
mp[sta]=(node){s,l};
}
}
for(int s=0;s<tmp2;s++)
{
int l=0,m=0,w=0,st=s;
for(int i=x+1;i<=n;i++)
{
if(st%3==0) l+=a[i],m+=b[i];
if(st%3==1) l+=a[i],w+=c[i];
if(st%3==2) m+=b[i],w+=c[i];
st/=3;
}
ll sta=(m-l)*p+(w-l);
// printf("%d %d %d %d %lld\n",s,l,m,w,sta);
if(mp.count(sta))
{
int tmp=l+mp[sta].val;
if(tmp>mx)
{
mx=tmp,s1=mp[sta].st,s2=s;
}
fl=1;
}
}
if(!fl) printf("Impossible");
else print(s1,s2);
return 0;
}
Make Them Similar
https://www.gxyzoj.com/d/hzoj/p/4371
一个储存前-后,另一个储存后-前,相同为相等则满足条件
点击查看代码
#include<cstdio>
#include<map>
#include<vector>
using namespace std;
int n,a[105],tmp=1<<15;
int get(int x)
{
int res=0;
while(x)
{
if(x&1) res++;
x>>=1;
}
return res;
}
map<vector<int>,int> mp;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int s=0;s<tmp;s++)
{
vector<int> x;
x.clear();
for(int i=2;i<=n;i++)
{
int t1=get((a[i]&(tmp-1))^s);
int t2=get((a[i-1]&(tmp-1))^s);
x.push_back(t2-t1);
}
if(!mp.count(x)) mp[x]=s;
}
for(int s=0;s<tmp;s++)
{
vector<int> x;
x.clear();
for(int i=2;i<=n;i++)
{
int t1=get((a[i]&((1<<30)-tmp))^(s<<15));
int t2=get((a[i-1]&((1<<30)-tmp))^(s<<15));
x.push_back(t1-t2);
}
if(mp.count(x))
{
printf("%d\n",(s<<15)+mp[x]);
return 0;
}
}
printf("-1");
return 0;
}
Prime Gift
16个数,每个数约15种情况,显然折半搜索,将所有的数分成两段,储存所有可以组成的数
分别排序后,二分求第k个即可,具体就是二分值域,统计有多少个乘积小于等于mid
但是还没有结束,如果你是从中间断开,则CF会给你一个TLE
别问我是怎么知道的
因为小的数情况多,大的情况少,check的复杂度和两个dfs的复杂度的主要组成都是两组的数量之和
因为整个序列能组成的数的数量一定,由均值不等式,差越小越优
所以可以按模2的余数去选,减少两组的数量之和
点击查看代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,a[20],a1[10],a2[10],n1,n2,cnt1,cnt2;
ll p1[10][100],p2[10][100];
ll k,p=1e18,v1[7400005],v2[7400005];
void dfs1(int now,ll val)
{
if(now==n1+1)
{
v1[++cnt1]=val;
return;
}
dfs1(now+1,val);
for(int i=1;;i++)
{
if(p/p1[now][i]<val) break;
dfs1(now+1,val*p1[now][i]);
}
}
void dfs2(int now,ll val)
{
if(now==n2+1)
{
v2[++cnt2]=val;
return;
}
dfs2(now+1,val);
for(int i=1;;i++)
{
if(p/p2[now][i]<val) break;
dfs2(now+1,val*p2[now][i]);
}
}
bool check(ll x)
{
ll cnt=0;
for(int i=1,j=cnt2;i<=cnt1&&j>0;i++)
{
if(v1[i]>x) break;
while(v1[i]>x/v2[j])
{
j--;
}
cnt+=j;
}
// printf("%lld %lld\n",x,cnt);
if(cnt>=k) return 1;
return 0;
}
int main()
{
scanf("%d",&n);
n1=(n+1)/2,n2=n-n1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(i%2) a1[(i+1)/2]=a[i];
else a2[i/2]=a[i];
}
for(int i=1;i<=n1;i++)
{
p1[i][0]=1;
for(int j=1;;j++)
{
if(p/a1[i]<p1[i][j-1])
{
p1[i][j]=p+1;
break;
}
else p1[i][j]=p1[i][j-1]*a1[i];
}
}
for(int i=1;i<=n2;i++)
{
p2[i][0]=1;
for(int j=1;;j++)
{
if(p/a2[i]<p2[i][j-1])
{
p2[i][j]=p+1;
break;
}
else p2[i][j]=p2[i][j-1]*a2[i];
}
}
scanf("%lld",&k);
dfs1(1,1);
dfs2(1,1);
sort(v1+1,v1+cnt1+1);
sort(v2+1,v2+cnt2+1);
// printf("%d %d\n",cnt1,cnt2);
ll l=1,r=p;
while(l<r)
{
ll mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%lld",l);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通