首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 其他相关 >

POJ 3925 - 状态DP.位演算

2012-09-16 
POJ 3925 - 状态DP.位运算研读北大的ACM国际大学生程序设计竞赛亚洲区域真题题解发现这题的...书上介绍

POJ 3925 - 状态DP.位运算

    研读北大的<ACM国际大学生程序设计竞赛亚洲区域真题题解>发现这题的...书上介绍的是DFS枚举点..然后最小生成树来找答案...正好前不久做过一些状态 DP的问题..就用状态DP水过了...

     对于一类点个数为n=15左右的问题...应该敏感的联想到状态DP...用n位2进制数可以在较好的所有点的状态...此题正是如此...用x ( 0<=x<=2^n) 表示当前的树中有哪些点...

     这里用到了两个位运算..

     一个是判断十进制整数x在二进制下的第k位是否为1...用 x & (1<<k-1) 来判断...如果是1结果为非0正整数...

     另一个是判断 十进制整数x在二进制下有多少位为1...x=x & (x-1) 要执行到x==0的次数即为x中1的个数..原因..因为总能去掉最末尾的1

      其实写完之后...发现本质上枚举点做最小生成树和我这种状态DP是一样的...因为我在更新过程中就是Prim的过程...


Program:

#include<iostream>#include<stdio.h>#include<algorithm>#include<string.h>#include<math.h>#include<map>#include<queue>#include<stack>#define ll long long#define oo 1000000000#define pi acos(-1)using namespace std;    int n,m,p[20],arc[20][20],g,ans,dp[40000]; double minimal;int main(){       int i,x,y,k,w,z,v;     while (~scanf("%d%d",&n,&m))     {           if (!n && !m) break;           for (i=1;i<=n;i++) scanf("%d",&p[i]);           for (y=1;y<=n;y++)              for (x=1;x<=n;x++)                  scanf("%d",&arc[y][x]);           g=(1<<n)-1;           memset(dp,-1,sizeof(dp));           dp[0]=0;           minimal=1e+20;           for (x=1;x<=g;x++)           {                v=0;                for (k=1;k<=n;k++)                   if (x & (1<<k-1)) // 判断第k位是否为1                    {                          v+=p[k];                          w=x-(1<<k-1);                          for (y=1;y<=n;y++)                            if (w & (1<<y-1))                              if (dp[x]==-1 || dp[x]>dp[w]+arc[y][k])                                  dp[x]=dp[w]+arc[y][k];                   }                k=0;                z=x;                while (z)                {                      k++;                      z=z & (z-1);                } // 得到当前状态下有多少个点..                 if (k==m && minimal>(dp[x]*1.0/v))                {                        minimal=dp[x]*1.0/v;                        ans=x;                }           }           for (x=1;x<=n;x++)              if (ans & (1<<x-1)) printf("%d ",x);           printf("\n");     }     return 0;}

热点排行