牛客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,再枚举从哪个点走来,走过哪座桥,走向哪个点,转移就可以了.

注意走过一个桥有两种走法(ABBA).

总时间复杂度为\(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分.暴力都调不出来,我实在是太蒻了.

posted @ 2019-11-08 10:01  TaylorSwift13  阅读(194)  评论(0编辑  收藏  举报