考场上T2想到正解,却没时间打,死磕在T1,心好累(DeBug下午+晚上。
T1 欠款
题目大意:\(n\)个人互相欠款,求还款的最小次数。\((n\le 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\n",ans);
}
return 0;
}
写的好简洁呀!咕咕咕~