题面:
得分情况:
35分枚举全排列暴力。。。
正解:
我们将直接与1相连的点称为a,不与1直接相连而与a直接相连的点称为b。
状压dp[i]表示已选的a,转移时枚举现在要选的a点,计算对答案的影响。(之前已选的点不管怎么排影响都已经计算过了)复杂度为
O(2A∗B)
O
(
2
A
∗
B
)
(A,B表示a,b点的个数),能拿到70分。
对于剩下的点,我们选取A,B中较小的来作为状态,如果选到了b点则类似地转移,复杂度为
O(2min(A,B)∗max(A,B))
O
(
2
m
i
n
(
A
,
B
)
∗
m
a
x
(
A
,
B
)
)
,能过掉所有数据。
代码:
#include <bits/stdc++.h>
using namespace std;
const int inf=1e5;
int dp[1<<23],map1[50][50],id[50];
long long con[50],dev[50];
int c0,c1,n,m,ans;
int main()
{
// freopen("friends.in","r",stdin);
// freopen("friends.out","w",stdout);
scanf("%d%d",&n,&m);
memset(map1,-1,sizeof(map1));
memset(id,-1,sizeof(id));
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
scanf("%d",&map1[x-1][y-1]);
map1[y-1][x-1]=map1[x-1][y-1];
}
for(int i=1;i<n;i++) if(map1[0][i]!=-1)
{
id[i]=c0++;
ans+=map1[0][i]^1;
for(int j=1;j<n;j++)
{
if(map1[0][j]==-1&&map1[i][j]!=-1)
{
if(id[j]==-1) id[j]=c1++;
con[id[i]]|=1ll<<id[j];
dev[id[i]]|=(long long)(map1[0][i]^map1[i][j]^1)<<id[j];
}
}
}
if(c0<=20)
{
for(int i=0;i<(1<<c0);i++) dp[i]=-inf;
dp[0]=0;
for(int i=0;i<(1<<c0);i++) if(dp[i]>=0)
{
long long cur=0;
for(int j=0;j<c0;j++) if((((i)>>(j))&1)==1) cur|=con[j];
for(int j=0;j<c0;j++) if((((i)>>(j))&1)==0)
{
int next=i|(1<<j),num=dp[i]+__builtin_popcountll(dev[j]&(~cur));
if(dp[next]<num) dp[next]=num;
}
}
ans+=dp[(1<<c0)-1];
}
else
{
for(int i=0;i<(1<<c1);i++) dp[i]=-inf;
dp[0]=0;
for(int i=0;i<(1<<c1);i++) if(dp[i]>=0)
{
for(int j=0;j<c0;j++) if((i|con[j])!=i)
{
int next=(i|con[j]),num=dp[i]+__builtin_popcountll(dev[j]&(~i));
if(dp[next]<num) dp[next]=num;
}
}
ans+=dp[(1<<c1)-1];
}
printf("%d\n",ans);
return 0;
}