题目:
题意:
有一张
n
∗
m
n*m
n∗m的图,每个节点都有
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;
}