[JOI2014] 小笼包

题面 : https://www.ioi-jp.org/joi/2013/2014-yo/2014-yo-t6/2014-yo-t6.html

题解

dp + 康托展开

一看这题不知道怎么处理

只能枚举顺序来处理小笼包的价值

然后又发现这个\(d[]\)只有7

所以可以状压他们的顺序

现学的康托展开将前7个的顺序存储起来

\(f[i][S]\)表示到第i个小笼包,包括第i个小笼包的前7个的先后顺序是什么

然后枚举第i+1个小笼包的顺序

算下对前面的贡献和前面的对ta的贡献

暴力dp即可

时间复杂度\(O(7!*n*7)\)

可以通过此题

代码


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 105 ;
const int N = 5050 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n ;
int v[M] , d[M] , Ans ;
int fac[9] , f[M][N] ;
int p[9] , w[9] ;
bool vis[9] ;
inline int Merge() {
    int Ans = 0 ;
    for(int i = 1 , ret ; i <= 7 ; i ++) {
        ret = 0 ;
        for(int j = i + 1 ; j <= 7 ; j ++)
            if(w[i] > w[j])
                ++ ret ;
        Ans += ret * fac[7 - i] ;
    }
    return Ans + 1 ;
}
inline void Split(int x) {
    memset(vis , false , sizeof(vis)) ;
    x -= 1; 
    for(int i = 1 , ret , now ; i <= 7 ; i ++) {
        ret = x / fac[7 - i] ;
        for(now = 1 ; now <= n ; now ++)
            if(!vis[now]) {
            	if(!ret) break ;
            	ret -- ;
            }
        p[i] = now ;
        vis[now] = true ;
        x %= fac[7 - i] ;
    }
}
int main() {
    n = read() ;
    for(int i = 1 ; i <= n ; i ++) d[i] = read() ;
    for(int i = 1 ; i <= n ; i ++) v[i] = read() ;
    fac[0] = 1 ;
    for(int i = 1 ; i <= 7 ; i ++) fac[i] = fac[i - 1] * i ;
    for(int i = 1 ; i <= n ; i ++)
        for(int S = 1 ; S <= fac[7] ; S ++) {
        	Split(S) ;
        	for(int j = 1 , ret ; j <= 8 ; j ++) { // ö¾ÙµÚi+1λµÄ˳Ðò 
        	    ret = 0 ;
                for(int k = 1 ; k <= 7 ; k ++)
        	        if(p[k] < j) w[k] = p[k] ;
        	        else w[k] = p[k] + 1 ;
        	    w[8] = j ;
        	    for(int k = 1 ; k <= 7 ; k ++)
        	        if((i - (7 - k)) > 0 && w[k] < w[8] && d[i - (7 - k)] >= 8 - k)
        	            ret += v[i - (7 - k)] ;
        	    for(int k = 1 ; k <= 7 ; k ++)
        	        if((i - (7 - k)) > 0 && w[k] > w[8] && d[i + 1] >= 8 - k)
        	            ret += v[i + 1] ;
        	    for(int k = 2 ; k <= 8 ; k ++)
        	        if(w[k] > w[1]) w[k] -- ;
        	    for(int k = 1 ; k <= 7 ; k ++)
        	        w[k] = w[k + 1] ;
        	    int x = Merge() ;
        	    f[i + 1][x] = max(f[i + 1][x] , f[i][S] + ret) ;
            }
        }
    for(int i = 1 ; i <= fac[7] ; i ++) Ans = max(Ans , f[n][i]) ;
    printf("%d\n",Ans) ;
    return 0 ;
}
posted @ 2018-10-25 17:40  beretty  阅读(283)  评论(0编辑  收藏  举报