解题报告:LightOJ_1406 状压DP

来源:互联网 发布:unity3d 数据库 编辑:程序博客网 时间:2024/05/21 06:57

题目链接

题意:

给定一张有向图,问最少能拆成几条路径要求包含所有点 且 不同路径之间没有重点,同一可以重复经过同一点(点数<=15,边数<=40)

思路:

定义

ok[x][y]:x集合是否存在一条以y点结尾的路径

dp[x]   :x集合的最少路径数

dp[x] = min( dp[i] + dp[j] )      i^j==x && i&j==0

因为有环,那么每次处理出一个可行的ok[x][y]时,dfs()找x集合其他可行的结束点

代码:

#include<bits/stdc++.h>using namespace std;bool G[15][15];bool ok[1<<15][15];int dp[1<<15];bool check(int x,int t){   for(int i=0;i<15;i++)if(ok[x][i]&&G[i][t])      return true;   return false;}void Dfs(int x,int t){   for(int i=0,j=1;j<=x;i++,j<<=1)      if((x&j)&&G[t][i]&&!ok[x][i]){         ok[x][i] = true;         Dfs(x,i);      }}void work(int x){   dp[x] = 0;   for(int i=0,j=1;j<=x;i++,j<<=1){      if((j&x) && !ok[x][i] ){         int t = j^x;         if(!t||check(t,i)){            ok[x][i]=true;            Dfs(x,i);         }      }if(ok[x][i])dp[x]=1;   }}int main(){   int T,Cas=0;scanf("%d",&T);   while(T--){      memset(G,0,sizeof(G));      memset(ok,0,sizeof(ok));      ok[0][0]=true;      int n,m;scanf("%d%d",&n,&m);      while(m--){         int s,t;scanf("%d%d",&s,&t);--s;--t;         G[s][t]=1;      }int ed = (1<<n);      for(int i=1;i<ed;++i)work(i);      for(int i=1;i<ed;++i)if(!dp[i]){dp[i] = 100;         for(int j=i;j;j=i&(j-1))            dp[i] = min(dp[i],dp[j]+dp[i^j]);      }printf("Case %d: %d\n",++Cas,dp[ed-1]);   }return 0;}


原创粉丝点击