牛客CSP-S提高组赛前集训营5 赛后总结
A.无形的博弈
心理题.
答案为\(2^n\),可感性理解结论的正确性.
#include<bits/stdc++.h>
#define LL long long
const LL Mod=998244353;
int n;
int main()
{
scanf("%d",&n);
LL Ans=1;
for(int i=1;i<=n;i++)
Ans=(Ans<<1)%Mod;
printf("%lld",Ans);
return 0;
}
B.十二桥问题
并没有做出来这道\(\text{*B}\)题,😔.
本题的第一个\(\text{trick}\)是:只有极少的点(桥的端点和节点1)真正有用.这是因为按照题意,所有的走动一定是从这些点中的一个走向另外一个.
这些"关键点"的个数不会超过25.我们可以跑25次 Dijkstra ,求出这些点两两之间的最短路.
然后呢?
我考试的时候写了%你退火
,凭借超好的运气获得了48
分的好成绩,还没有枚举子集的暴力高😢.
正解是状态压缩DP
,设DP[i][Statu]
表示在第i
个"关键点",已经走过的桥的状态为Statu
时,最小的总距离.枚举Statu
,再枚举从哪个点走来,走过哪座桥,走向哪个点,转移就可以了.
注意走过一个桥有两种走法(A
到B
和B
到A
).
总时间复杂度为\(O(k\times n\log E+2^k\times k^3)\).
#include<bits/stdc++.h>
#define LL long long
const int N_MAX=50005,M_MAX=400005,SIZE=25+1;
int n,m,k,head[N_MAX],nex[M_MAX],to[M_MAX],edge[M_MAX],Tot;
void Link(int u,int v,int e)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;edge[Tot]=e;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;edge[Tot]=e;
}
struct Line
{
int A,B;
LL D;
}L[SIZE];
LL D[SIZE][SIZE];
int Raw[SIZE],F[N_MAX],Cnt;
void new_node(int x)
{
if(F[x])return;
Raw[++Cnt]=x;
F[x]=Cnt;
}
LL Dx[N_MAX];
bool mk[N_MAX];
struct node
{
int pos;
LL D;
bool operator <(const node &x)const
{
return D>x.D;
}
};
std::priority_queue<node>q;
void Dij(int S)
{
memset(Dx,0x3F,sizeof(Dx));
memset(mk,0,sizeof(mk));
Dx[S]=0;
q.push((node){S,Dx[S]});
while(q.size())
{
int u=q.top().pos;
q.pop();
if(mk[u])continue;
mk[u]=1;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(Dx[v]>Dx[u]+edge[i])
{
Dx[v]=Dx[u]+edge[i];
q.push((node){v,Dx[v]});
}
}
}
}
LL DP[SIZE][1<<13];
int main()
{
scanf("%d%d%d",&n,&m,&k);
int u,v,e;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&e);
Link(u,v,e);
if(i<=k)
{
new_node(u);
new_node(v);
L[i]=(Line){F[u],F[v],e};
}
}
new_node(1);
for(int i=1;i<=Cnt;i++)
{
Dij(Raw[i]);
for(int k=1;k<=Cnt;k++)
D[i][k]=Dx[Raw[k]];
}
memset(DP,0x3F,sizeof(DP));
DP[F[1]][0]=0;
for(int Statu=0;Statu<(1<<k);Statu++)
{
for(int i=1;i<=Cnt;i++)
{
for(int p=1;p<=k;p++)
{
for(int q=1;q<=Cnt;q++)
{
DP[i][Statu|(1<<(p-1))]=std::min(DP[i][Statu|(1<<(p-1))],DP[q][Statu]+D[q][L[p].A]+L[p].D+D[L[p].B][i]);
DP[i][Statu|(1<<(p-1))]=std::min(DP[i][Statu|(1<<(p-1))],DP[q][Statu]+D[q][L[p].B]+L[p].D+D[L[p].A][i]);
}
}
}
}
printf("%lld",DP[F[1]][(1<<k)-1]);
return 0;
}
神J上树
最后6
秒交了一发暴力,还好得了40
分.暴力都调不出来,我实在是太蒻了.