zoukankan      html  css  js  c++  java
  • 「GXOI / GZOI2019」宝牌一大堆 (DP)

    题意

    LOJ传送门

    题解

    可以发现「七对子」 和 「国士无双」直接暴力就行了。

    唯一的就是剩下的"3*4+2"。

    考试的时候写了个爆搜剪枝,开了O2有50pts。写的时候发现可以DP,但是没写。

    然后下来写了发现就4个转移。。。

    (dp[i][j][k][a][b][c])表示当考虑前(i)张牌,有(j)个雀头,(k)个面子,(i-2)用了(a)张,(i-1)用了(b)张,(i)用了(c)张时,前(i-3)张牌的最大分数。

    注意这里是"前(i-3)张牌的最大分数",也就是没有计算(a,b,c)这最后(3)种牌的贡献。

    有个性质就是杠子一定不会选,因为选杠子一定劣于面子。
    因为(C(4,4)=1<C(4,3)=4),就算这张牌是宝牌,杠子的贡献是(C(4,4)*2^4<C(4,3)*2^3)

    所以我们没有考虑杠子。

    上代码

    CODE

    luogu上开了O2才过

    #include <bits/stdc++.h>
    using namespace std;
    inline int getid(char *s) {
    	if(isdigit(s[0])) {
    		int re = s[0]-'0';
    		if(s[1] == 'm') return re;
    		if(s[1] == 'p') return 9+re;
    		if(s[1] == 's') return 18+re;
    	}
    	if(s[0] == 'E') return 28;
    	if(s[0] == 'S') return 29;
    	if(s[0] == 'W') return 30;
    	if(s[0] == 'N') return 31;
    	if(s[0] == 'Z') return 32;
    	if(s[0] == 'B') return 33;
    	if(s[0] == 'F') return 34;
    }
    typedef long long LL;
    int cnt[50], bel[50];
    LL ans, c[5][5], pw6[10];
    bool bao[50];
    
    int arr13[14] = { 0, 1, 9, 10, 18, 19, 27, 28, 29, 30, 31, 32, 33, 34 }; //指定的13张牌的编号
    void solve13() {
    	bool flg = 1;
    	for(int i = 1; i <= 13; ++i) flg &= bool(cnt[arr13[i]]);
    	if(flg) {
    		LL now = 13;
    		for(int i = 1; i <= 13; ++i)
    			now = now * c[cnt[arr13[i]]][1] * (bao[arr13[i]] ? 2 : 1);
    		for(int i = 1; i <= 13; ++i)
    			ans = max(ans, now / c[cnt[arr13[i]]][1] * c[cnt[arr13[i]]][2] * (bao[arr13[i]] ? 2 : 1));
    	}
    }
    
    void dfs(int i, int j, LL now) { //暴力枚举七对子
    	if(j == 7) { ans = max(now, ans); return; }
    	if(i > 34) return;
    	LL tmp = now*pw6[7-j]*(1ll<<((7-j)<<1)); //剪枝,预估之后的最大值,如果小于等于当前答案就不用了搜下去了
    	if(tmp <= ans) return;
    	if(cnt[i] >= 2) {
    		cnt[i] -= 2;
    		dfs(i+1, j+1, now*c[cnt[i]+2][2]*(bao[i]?4:1));
    		cnt[i] += 2;
    	}
    	if(tmp <= ans) return;
    	dfs(i+1, j, now);
    }
    void solve7() { dfs(1, 0, 7); }
    
    LL dp[36][2][5][5][5][5];
    inline void upd(LL &x, LL y) { x = y > x ? y : x; }
    inline int calc(int i, int j) { return i > 0 ? c[cnt[i]][j] * (bao[i] ? (1<<j) : 1) : 1; } //计算i选j张的贡献
    void solve() {
    	memset(dp, 0, sizeof dp);
    	dp[1][0][0][0][0][0] = 1;
    	for(int i = 1; i <= 34; ++i)
    		for(int j = 0; j < 2; ++j)
    			for(int k = 0; k <= 4; ++k)
    				for(int a = 0; a <= 4; ++a)
    					for(int b = 0; b <= 4; ++b)
    						for(int c = 0; c <= 4; ++c) {
    							LL val = dp[i][j][k][a][b][c];
    							if(val) {
    								upd(dp[i+1][j][k][b][c][0], val * calc(i-2, a)); 
    								if(!j && cnt[i]-c >= 2)
    									upd(dp[i][j+1][k][a][b][c+2], val);
    								if(k<4 && cnt[i]-c >= 3)
    									upd(dp[i][j][k+1][a][b][c+3], val);
    								if(k<4 && bel[i] && i>2 && bel[i]==bel[i-2] && cnt[i]-c >= 1 && cnt[i-1]-b >= 1 && cnt[i-2]-a >= 1)
    									upd(dp[i][j][k+1][a+1][b+1][c+1], val);
    							}
    					}
    	for(int a = 0; a <= 4; ++a)
    		for(int b = 0; b <= 4; ++b)
    			for(int c = 0; c <= 4; ++c) //最后答案算上a,b,c
    				upd(ans, dp[34][1][4][a][b][c] * calc(32, a) * calc(33, b) * calc(34, c)); 
    }
    
    int main () {
    	//freopen("doraippai.in", "r", stdin);
    //	freopen("doraippai.out", "w", stdout);
    	for(int i = 1; i <= 9; ++i) bel[i] = 1;
    	for(int i = 10; i <= 18; ++i) bel[i] = 2;
    	for(int i = 19; i <= 27; ++i) bel[i] = 3;
    	c[0][0] = 1;
    	for(int i = 1; i <= 4; ++i) {
    		c[i][0] = c[i][i] = 1;
    		for(int j = 1; j < i; ++j)
    			c[i][j] = c[i-1][j-1] + c[i-1][j];
    	}
    	pw6[0] = 1;
    	for(int i = 1; i <= 7; ++i) pw6[i] = 6ll * pw6[i-1];
    	int T; scanf("%d", &T); while(T--) {
    		for(int i = 1; i <= 34; ++i)
    			cnt[i] = 4, bao[i] = 0;
    		char s[5];
    		while(scanf("%s", s), s[0] != '0') {
    			int num = getid(s);
    			--cnt[num];
    		}
    		while(scanf("%s", s), s[0] != '0') {
    			int num = getid(s);
    			bao[num] = 1;
    		}
    		ans = 0;
    		solve13();
    		solve7();
    		solve();
    		printf("%lld
    ", ans);
    	}
    }
    
  • 相关阅读:
    码农的半衰期只有15年?
    微软面试100题2010年版全部答案集锦(转自July)
    大量url,如何去重
    后缀树求最长子字符串
    转 STL hash_map & map
    有n 个长为m+1 的字符串,求前后m个字符匹配所能形成的最长字符串链:利用弗洛伊德算法求最长路径
    获取本机地址信息,遇到小问题...有待解决
    HDOJ 1006
    归并排序
    插入排序的简单实现
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12051413.html
Copyright © 2011-2022 走看看