JZOJ 6313.Maja【dp】


题目:

传送门


题意:

有一张 n ∗ m n*m nm的图,每个节点都有 c i , j c_{i,j} ci,j朵花可以授粉,当你授粉某个位置的粉并离开该位置时,此时那里又会出现 c i , j c_{i,j} ci,j朵待授粉的花
现在指定起点,求在走恰好 k k k步且终点是在起点的最多授粉花数


分析:

首先我们想一下一条最优的路径的组成,肯定是走到某一个点,然后在几个点间反复走,最后原路返回
首先原路返回肯定是最优的,如果存在另一条可以使答案更大的路径使得我们可以返回起点,那么我们为什么不来的时候就走那条路呢
再一个性质,就是反复走的点的数量一定是 2 2 2,证明原理跟上面一条类似,大家手玩感性理解下
f k , i , j f_{k,i,j} fk,i,j表示走到 i , j i,j i,j时用了恰好 k k k步且此时开始反复走,有了上面两个性质我们就可以很好的写 d p dp dp


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define LL long long 
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
LL t[50][50],n,m,tf[50];
LL c1,c2,id[50];
LL w[50],zt[50],f[(1<<23)];
int main()
{
	freopen("friends.in","r",stdin);
	freopen("friends.out","w",stdout);
	n=read(),m=read();
	memset(t,-1,sizeof(t));memset(id,-1,sizeof(id));
	for(LL i=1;i<=m;i++)
	{
		LL x=read(),y=read();
		t[x][y]=t[y][x]=read();
	}
	LL ans=0;
	for(LL i=2;i<=n;i++)
	{
		if(t[1][i]==-1) continue;
		id[i]=c1++;ans+=(t[1][i]^1);
		for(LL j=2;j<=n;j++)
		{
			if(t[1][j]==-1&&t[i][j]!=-1)
			{
				if(id[j]==-1) id[j]=c2++;
				zt[id[i]]|=(1ll<<id[j]);
				w[id[i]]|=((t[1][i]^t[i][j]^1)<<id[j]);
			}
		}
	}
	memset(f,0xcf,sizeof(f));
	f[0]=0;
	if(c1<=20)
	{
		for(LL i=0;i<(1<<c1);i++)
		{
			if(f[i]<0) continue;
			LL bc=0;
			for(LL j=0;j<c1;j++) if((i>>j)&1) bc|=zt[j];
			for(LL j=0;j<c1;j++)
			{
				if(i>>j&1) continue;
				LL next=(i|(1<<j)),s=f[i]+__builtin_popcountll(w[j]&(~bc));
				f[next]=max(f[next],s);
			}
		}
		ans+=f[(1<<c1)-1];
	}
	else
	{
		for(LL i=0;i<(1<<c2);i++)
		{
			if(f[i]<0) continue;
			for(LL j=0;j<c1;j++)
			{
				if(((i|zt[j])==i)) continue;
				LL next=(i|zt[j]),s=f[i]+__builtin_popcountll(w[j]&(~i));
				f[next]=max(f[next],s);
			}
		}
		ans+=f[(1<<c2)-1];		
	}
	cout<<ans;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值