8-22模拟赛解题报告

考场上T2想到正解,却没时间打,死磕在T1,心好累(DeBug下午+晚上。


T1 欠款

题目大意:(n)个人互相欠款,求还款的最小次数。((nle 23))

这题比较特别,是一个基本 (不会的) 状压DP,考场上打了一个(floied)骗了(80)分左右。

因为一个连通块内如果欠款数之和正好为(0),说明该连通块是可以自己做好不需要他人参与的,所以(f[i])表示该状态下联通块的最大数量,每次加入一个点更新,最后用(n-f[2^n-1])就是答案。

小结:

解题关键:想到状压DP状态

芝士点:状压DP

整体来说不算太难,考场想错方向了。

手起,码落:

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define re register
using namespace std;
const int N=25;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
int n,m,val[N],f[(1<<N)+5];
int main()
{
	scanf("%d%d",&n,&m);
	for(re int i=1,u,v,Val;i<=m;i++)
	{
		u=read(),v=read(),Val=read();
		val[u]+=Val,val[v]-=Val;
	}
	for(re int i=1,sum=0;i<(1<<n);i++,sum=0)
	{
		for(re int j=0;j<n;j++)
			if(i&(1<<j))
				f[i]=max(f[i],f[i-(1<<j)]),sum+=val[j+1];
		f[i]+=(sum==0);
	}
	printf("%d",n-f[(1<<n)-1]);
	return 0;
}

T2:子树

题目大意: 一棵树取两棵子树,求两颗子树的最大权值和。

还是比较好想的,建虚点把基环树变成普通树来做,然后就是基本换根DP了。

手起,码落:

#include<bits/stdc++.h>
#define re register
#define inf 1e18
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
const int N=1000005;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
struct edge{int v,net;}e[N<<1];
int T,n,m,cnt,hd[N],s,in[N],fa[N];
long long m1[N],m2[N],ans,ma_son[N],ma_down[N],ma_up[N],sum[N],a[N];
bool vis[N];
inline void add(int u,int v)
{
	in[u]++,in[v]++;
	e[++cnt].v=v,e[cnt].net=hd[u],hd[u]=cnt;
	e[++cnt].v=u,e[cnt].net=hd[v],hd[v]=cnt;
}
queue <int> q;
inline void topu()
{
	for(;!q.empty();q.pop());
	for(re int i=1;i<=n;i++)
		if(in[i]==1)q.push(i),in[i]--;
	for(re int u;!q.empty();)
	{
		u=q.front();q.pop();
		for(re int v,i=hd[u];i;i=e[i].net)
		{
			v=e[i].v;
			if(in[v]==0)continue;
			in[v]--;
			if(in[v]==1)q.push(v);
		}
	}
	for(re int u=1;u<=n;u++)
	{
		if(in[u]!=2)continue;
		vis[u]=1,add(u,s);
	}
}
void first(int u)
{
	ma_son[u]=sum[u]=a[u];
	for(re int v,i=hd[u];i;i=e[i].net)
	{
		v=e[i].v;
		if(v==fa[u]||(vis[v]&vis[u]))continue;
		fa[v]=u,first(v);
		sum[u]+=sum[v];
		if(sum[v]>0)ma_son[u]+=sum[v];
	}
	if(u==s)ma_son[u]=-inf;
	m1[u]=m2[u]=-inf;
	for(re int v,i=hd[u];i;i=e[i].net)
	{
		v=e[i].v;
		if(v==fa[u]||(vis[v]&vis[u]))continue;
		if(ma_down[v]>m1[u])m2[u]=m1[u],m1[u]=ma_down[v];
		else if(m1[v]>m2[u])m2[u]=ma_down[v];
	}
	ma_down[u]=max(ma_son[u],m1[u]);
}
void dfs(int u)
{
	for(re int v,i=hd[u];i;i=e[i].net)
	{
		v=e[i].v;
		if(fa[u]==v||(vis[u]&&vis[v]))continue;
		if(u==s)ma_up[v]=-inf;
		else ma_up[v]=ma_son[u]+(sum[1]-sum[u]<=0?0:sum[1]-sum[u])-(sum[v]<=0?0:sum[v]);
		if(fa[u]!=-1)ma_up[v]=max(ma_up[v],ma_up[u]);
		if(ma_down[v]==m1[u])ma_up[v]=max(ma_up[v],m2[u]);
		else ma_up[v]=max(ma_up[v],m1[u]);
		dfs(v);
	}
	if(u==s)return ;
	if(fa[u]!=-1)ans=max(ans,ma_son[u]+ma_up[u]);
	for(re int v,i=hd[u];i;i=e[i].net)
	{
		v=e[i].v;
		if(fa[u]==v||(vis[v]&&vis[u]))continue;
		ans=max(ans,ma_son[u]+ma_down[v]-(sum[v]<=0?0:sum[v])+(sum[1]-sum[u]<=0?0:sum[1]-sum[u]));
	}
}
int main()
{
	scanf("%d",&T);
	for(;T--;)
	{
		memset(hd,0,sizeof(hd)),cnt=0,ans=-inf;
		memset(fa,0,sizeof(fa));
		memset(vis,0,sizeof(vis));
		memset(in,0,sizeof(in));
		scanf("%d%d",&n,&m);s=1+n;
		for(re int i=1;i<=n;i++)scanf("%lld",&a[i]);
		for(re int i=1;i<=m;i++)add(read(),read());
		if(n==m)topu();
		fa[1]=-1;
		first(1);
		dfs(1);
		printf("%lld
",ans);
	}
	return 0;
}

写的好简洁呀!咕咕咕~

原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13574126.html