HDU6006(2016CCPC-Final H Engineer Assignment 状压dp)

在这里插入图片描述题意: n(n<=10)个工程 m(<=10)个工程师。每个工程需要一些技术(技术个数<=3) 一个工程师精通一些技术(技术个数<=2) 。一个工程师只能去一个工程干活,一个工程师可以担任一个工程里的多个技术。问最多多少个工程可以开工。技术种类数<=100。

思路: dp[i][S]: 前 i 个工程,S表示1-m个工程师选与不选的状态。
dp[i][S]=max(dp[i-1][S],dp[i][S^vec[i][k]]+1).
vec[i][k]是预处理出来哪种状态可以使得工程 i 完工。假如这种状态需要的人数>3,那么这种不放入vector 其实是为了优化,很显然啊,总共需要3个技术 怎么可能需要3个以上的人呢,这也对,但是没必要用这种状态转移dp。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define m(a,b) memset(a,b,sizeof a)
using namespace std;
const int N=1e5+5;
int a[11][4],b[11][3],vis[101],dp[11][(1<<10)];
vector<int>vec[11];
int main(){
    int T,cas=0;scanf("%d",&T);
    while(T--){
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i][0]);
            for(int j=1;j<=a[i][0];++j) scanf("%d",&a[i][j]);
        }
        for(int i=1;i<=m;++i){
            scanf("%d",&b[i][0]);
            for(int j=1;j<=b[i][0];++j) scanf("%d",&b[i][j]);
        }
        int t=(1<<m);
        for(int i=1;i<=n;++i){//将每个工程合法的需要的工程师的方案预处理
            for(int j=1;j<t;++j){
                m(vis,0);int num=0;
                for(int k=1;k<=m;++k){
                    if(j&(1<<(k-1))){
                        ++num;if(num>3) break;//人数不超过3个 相当于减少不优的转移状态
                        for(int o=1;o<=b[k][0];++o) vis[b[k][o]]=1;
                    }
                }
                int flag=0;
                for(int o=1;o<=a[i][0];++o)
                    if(!vis[a[i][o]]) {flag=1;break;}
                if(num<=3&&!flag) vec[i].push_back(j);
            }
        }
        int ans=0;
        for(int i=1;i<=n;++i){//dp[i][j]=max(dp[i-1][j],dp[i-1][j^vec[i][k]]+1);
            for(int j=0;j<t;++j){
                dp[i][j]=dp[i-1][j];
                for(int k=0;k<vec[i].size();++k)
                    if((j|vec[i][k])==j) dp[i][j]=max(dp[i][j],dp[i-1][j^vec[i][k]]+1);//j|vec[i][k]==j
            }
        }
        for(int j=0;j<t;++j) ans=max(ans,dp[n][j]);
        printf("Case #%d: %d\n",++cas,ans);
        if(T){
            for(int i=1;i<=n;++i) vec[i].clear();
            for(int j=0;j<t;++j) dp[0][j]=0;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值