HDOJ5691解题报告【状压DP】

题目地址:

  http://acm.hdu.edu.cn/showproblem.php?pid=5691

题目概述:

  中文题面就不赘述了。

大致思路:

  傻傻的以为是枚举所有情况再剪枝,事实上fact(16)已经有20922789888000这么大了,显然是无法通过剪枝来解决的了。

  n这么小除了暴力还可以枚举状态呀。这时很自然想到状压DP。

  因为有些熊的位置被限制并且最后一个数的取值会对后续状态产生影响,所以我们的状态应该设成dp[i]j]表示当前最后一位选择了i,选取的总状态为j的最优解,转移方程为:

              转移的条件为当前状态stat已经选择了j并且j能放在这个状态的最后一位并且stat没有选择i而最后一位可以放i。

  这里需要注意的是最外层循环要枚举stat而不是枚举i或者j,因为第i只熊的选择时间是不定的。

复杂度分析:

  根据状态转移方程很容易得出复杂度是

 

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <ctime>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

#define sacnf scanf
#define scnaf scanf
#define maxn 20
#define maxm 70000
#define inf 1061109567
#define INF 0x3f3f3f3f
#define Eps 0.000001
const double PI=acos(-1.0);
#define mod 1000000007
#define MAXNUM 10000
#define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++)
#define mes(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ulld;
void Swap(int &a,int &b) {int t=a;a=b;b=t;}
ll Abs(ll x) {return (x<0)?-x:x;}

int a[maxn],p[maxn];
int num[maxm];
int dp[maxn][maxm];

int calc(int x)
{
    int cnt=0;
    while(x)
    {
        if(x&1) cnt++;
        x>>=1;
    }
    return cnt;
}

int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    //clock_t st=clock();
    int T;scanf("%d",&T);
    for(int i=0;i<(1<<16);i++) num[i]=calc(i);
    For(kase,1,T)
    {
        printf("Case #%d:\n",kase);
        int n;scanf("%d",&n);
        For(i,1,n) scanf("%d%d",&a[i-1],&p[i-1]);
        for(int i=0;i<n;i++)
            for(int j=0;j<(1<<n);j++) dp[i][j]=-INF;
        for(int i=0;i<n;i++)
            if(p[i]==-1||p[i]==0) dp[i][1<<i]=0;
        for(int stat=1;stat<(1<<n);stat++)
        {
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<n;j++)
                {
                    int t=num[stat];if(dp[j][stat]<=-INF) continue;
                    if(((stat&(1<<j))==0)||((stat&(1<<i))!=0)) continue;
                    if(p[j]!=-1&&p[j]!=t-1) continue;
                    if(p[i]!=-1&&p[i]!=t) continue;
                    dp[i][stat|(1<<i)]=max(dp[i][stat|(1<<i)],dp[j][stat]+a[i]*a[j]);
                }
            }
        }
        int ans=-INF;
        for(int i=0;i<n;i++) ans=max(ans,dp[i][(1<<n)-1]);
        printf("%d\n",ans);
    }
    //clock_t ed=clock();
    //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC);
    return 0;
}

 

posted @ 2017-07-11 11:03  CtrlKismet  阅读(205)  评论(0编辑  收藏  举报