zoukankan      html  css  js  c++  java
  • [USACO14FEB] Cow Decathlon 牛的十项全能

    洛咕

    题意:约翰有N头奶牛,组成了一直队伍参加全能比赛。比赛一共有N项,每头奶牛必须参加一项比赛,每项比赛也必须有一头奶牛参加。任何一头奶牛可以胜任任何一项比赛,但得分不一样。如果第i头奶牛参加第j项比赛,在比赛结束的时候,可以为团体总分增加Si,j。 比赛是按照顺序依次进行的。除了上述获得分数的方法之外,还有B种奖励分。获得奖励的方法是在前几项比赛里获得足够的分数。具体来说,第i项奖励会在第Ki项比赛结束的时候检查,如果 当时的总分大于或等于Pi,奶牛们就可以立即获得额外的Ai 分。如果有多项奖励在同一时刻检查,奶牛可以自由安排检查和加分的顺序。请问约翰应该如何安排奶牛参加比赛,才能让它们获得最高的分数?(1≤N,B≤20,1≤Ki≤N ,1≤Pi≤40000,1≤Ai≤1000,1≤Si,j≤1000)

    分析:从下午做到晚上,状压的思路很明显,转移方程也很好写.设(f[i])表示当前已经安排好了的(参加了比赛)的奶牛集合状态为(i)时的最高分数.

    (f[i]=max(f[i],f[i)$(1$<<$(j-1))]+S[j][calc(i)]+sum[calc(i)][f[i$((1)<<((j-1))]+S[j][calc(i)]])).

    解释一下这个麻烦的式子,当前选择用第j头牛参加比赛,(calc(i))表示i的二进制数中为1的个数(即当前第j头牛参加的是第几项比赛),(sum[i][j])表示第i场比赛结束分数为j时能获得多少的额外奖励.

    为了不超时,(calc(i),sum[i][j])都必须预处理.调了好久才搞好.时间复杂度是(O(n2^n)).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=21;
    const int M=1050000;
    int s[N][N],b[M],bj[N],f[M],rev[M],tot[21],sum[N][40005];
    vector<pair<int,int> >g[N];
    inline int calc(int x){
    	int cnt=0;
    	while(x){++cnt;x^=(x&-x);}
    	return cnt;
    }
    int main(){
    	int n=read(),m=read();
    	for(int i=1;i<=m;++i){
    		int tim=read(),goal=read(),val=read();bj[tim]=1;tot[tim]+=val;
    		g[tim].push_back(pair<int,int>(goal,val));
    	}
    	for(int i=1;i<=n;++i)//按照获得额外奖励的要求值从小到大排序
    		if(g[i].size())sort(g[i].begin(),g[i].end());
    	for(int i=1;i<=n;++i){//预处理sum数组
    		if(!bj[i])continue;
    		for(int j=g[i][0].first;j<=40000;++j){//题目说了要求值最大为40000
    			int cnt=0,x=j;
    			for(int k=0;k<(int)g[i].size();++k){
    				if(x>=g[i][k].first)cnt+=g[i][k].second,x+=g[i][k].second;
    				else break;//这个要求值不能满足,之后的更不会满足
    			}
    			sum[i][j]=cnt;
    			if(cnt==tot[i]){//简单优化一下,现在的分数j就已经能获得全部额外奖励的话,之后的j也全都能
    				for(int k=j+1;k<=40000;++k)sum[i][k]=cnt;
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)s[i][j]=read();
    	for(int i=1;i<(1<<n);++i)b[i]=calc(i);//预处理每个i中有多少1
    	int st=1;rev[1]=0;for(int i=1;i<=19;++i)st<<=1,rev[st]=i;//建立2^i与i的映射关系
    	for(int i=1;i<(1<<n);++i){
    		int ii=i;
    		while(ii){
    			int x=ii&-ii;//找到该状态下所有为1的位
    			f[i]=max(f[i],f[i^x]+s[rev[x]+1][b[i]]+sum[b[i]][min(f[i^x]+s[rev[x]+1][b[i]],40000)]);
    			ii^=x;
    		}
    	}
    	printf("%d
    ",f[(1<<n)-1]);
        return 0;
    }
    
    
  • 相关阅读:
    OpenRisc-52-run openrisc&orpmon on ml501 board
    PHP之APC缓存详细介绍(转)
    ios 使用GCD 多线程 教程
    poj2454
    尝鲜delphi开发android/ios_环境搭建
    HDU 3308 线段树 最长连续上升子序列 单点更新 区间查询
    jQuery 表格排序插件 Tablesorter 使用
    Oracle 常见错误
    安卓开发44:解决 INSTALL_FAILED_UID_CHANGED 等问题
    Java的native方法
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11740466.html
Copyright © 2011-2022 走看看