Rikka with Game Theory 小米邀请赛G题

本文介绍了一道关于状态压缩动态规划的经典问题及其解决方案。通过对题目进行深入分析,作者提出了一个巧妙的状态划分方法,将复杂度从3^n降低到了更优的形式。文章详细解释了如何利用预处理和子集枚举等技术来实现高效的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://ac.nowcoder.com/acm/contest/9328/G

觉得这题还挺好的,虽然2个半小时才过。。。。

这题巧妙在设计状态,如果直接dfs,我们发现有3种状态,一是已经被放了数字的点,二是可以放当前数字的点,三是需要放当前数字+1的点

那么就会变成3^n

但是我们知道最后要填满所有点,那么必须保证当前所有可以放当前数字的点都放完了以后,也就是状态二不存在了,我们才能开始考虑放下一个数字

也就是每个数字放完所有状态只剩下一三

所以我们就可以精简为两种状态,每次新增一些点使得点只剩一三状态

那么dp[s][i]表示第i个数字放完后,s这个点集已经被放完数字了,剩下的都是没放的

每次枚举要新增哪些点集的时候,设rs=s的补集,由于我们要把剩下的点都变成3状态,那么t必须被完全覆盖

也就是枚举rs的子集ns,通过预处理出来的ns能连出去的边覆盖的点集ks[ns],如果ks[ns]&rs==rs,说明只要把ns填满当前数字,就能把剩下的点全部变成三状态

预处理的时候还要判断ns是否内部是没有边的,否则ns这个点集就不行

虽然dp数组是n*2^n的,但是实际上只会有2^n的状态,因为每个s能放的数字实际上是固定的,由于要枚举子集,所以总复杂度是3^n的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=20;

int n,m,mx;ll ans;
ll dp[1<<18][20];
int es[1<<18],ks[1<<18];
vector<int> e[maxl];
bool hse[20][20];
bool nono[1<<18];

inline ll dfs(int s,int now)
{
  if(s==mx)
    return 1ll;
  if(dp[s][now]>=0)
    return dp[s][now];
  int z=0,rs=mx^s;
  dp[s][now]=0;
  for(int ns=rs;ns;ns=(ns-1)&rs)
    if(!nono[ns] && (ks[ns]&rs)==rs)
      dp[s][now]+=dfs(s|ns,now+1);
  return dp[s][now];
}

int main()
{
  //freopen("G.in","r",stdin);
  scanf("%d%d",&n,&m);
  mx=(1<<n)-1;
  for(int i=1;i<=n;i++)
    es[i]|=1<<(i-1);
  for(int i=1;i<=m;i++)
    {
      int u,v;
      scanf("%d%d",&u,&v);
      e[u].push_back(v);
      es[u]|=1<<(v-1);hse[u][v]=true;
      e[v].push_back(u);
      es[v]|=1<<(u-1);hse[v][u]=true;
    }
  for(int s=0;s<=mx;s++)
    {
      ks[s]=0;nono[s]=false;
      for(int i=1;i<=n;i++)
	if(s>>(i-1)&1)
	  {
	    for(int j=i+1;j<=n;j++)
	      if(s>>(j-1)&1)
		nono[s]|=hse[i][j];
	    ks[s]|=es[i];
	  }
    }
  memset(dp,-1,sizeof(dp));
  ans=dfs(0,0);
  printf("%lld",ans);
  return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值