• [HDU]5691Sitting in Line (状压DP)


    题意

    给定一列数,要求重新安排这列数,使得相邻两数的乘积之和最大。
    有一定限制:某些位置上只能填某个数。
    (nle16)

    题解

    很明显的状压复杂度。
    显然当前填的数只与上个数有关。
    一开始想以阶段作为状态(f[i][j][k])表示前i个位置,填的状态为k,最大的乘积。
    成功地T了这道题。

    然后考虑哪里有状态多余。
    发现其实直接去掉第一维的枚举就行了。
    因为在状压的过程中,都是用比较小的j去更新大的j(因为二进制中去掉一个1,数肯定变小)
    所以枚举这个位置是不必要的。
    直接对于每个j去更新就行了

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) >> 1)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    int read(){
    	char c; int num, f = 1;
    	while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    	while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    	return f * num;
    }
    const int inf = -1ll << 50;
    int n, f[1 << 16][17], vis[17], pos[17], num[17], cnt[1 << 16];
    void up(int &x, int y) {if(x < y) x = y;}
    void work(int cc) {
    	n = read();
    	for(int i = 0; i < n; i++) vis[i] = -1;
    	for(int i = 0; i < n; i++) num[i] = read(), pos[i] = read() + 1;
    	for(int j = 0; j < 1 << n; j++) for(int k = 0; k <= n; k++) f[j][k] = inf;
    	for(int i = 0; i < n; i++) if(!pos[i] || pos[i] == 1) f[1 << i][i] = 0;
    	for(int s = 0, t; s < (1 << n); s++, t = cnt[s] + 1) {
    		for(int j = 0; j < n; j++) if(f[s][j] > inf){
    			for(int k = 0; k < n; k++) {
    				if(j == k || s & (1 << k) || (pos[k] && pos[k] != t)) continue;
    				up(f[s | (1 << k)][k], f[s][j] + num[j] * num[k]);
    			}
    		}
    	}
    	int ans = inf;
    	for(int i = 0; i < n; i++) ans = max(ans, f[(1 << n) - 1][i]);
    	printf("Case #%lld:
    %lld
    ", cc, ans);	
    }
    signed main()
    {
    	for(int i = 1; i < 1 << 16; i++) 
    		cnt[i] = cnt[i >> 1] + (i & 1);
    	int Case = read(), cc = 0;
    	while(Case--) work(++cc);	
    	return 0;
    }
    /*
    f[i][j]表示前j个位置,坐法为i的最大大小是多少
    */
    

    题意

    给定一列数,要求重新安排这列数,使得相邻两数的乘积之和最大。
    有一定限制:某些位置上只能填某个数。
    (nle16)

    题解

    很明显的状压复杂度。
    显然当前填的数只与上个数有关。
    一开始想以阶段作为状态(f[i][j][k])表示前i个位置,填的状态为k,最大的乘积。
    成功地T了这道题。

    然后考虑哪里有状态多余。
    发现其实直接去掉第一维的枚举就行了。
    因为在状压的过程中,都是用比较小的j去更新大的j(因为二进制中去掉一个1,数肯定变小)
    所以枚举这个位置是不必要的。
    直接对于每个j去更新就行了

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) >> 1)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    int read(){
    	char c; int num, f = 1;
    	while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    	while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    	return f * num;
    }
    const int inf = -1ll << 50;
    int n, f[1 << 16][17], vis[17], pos[17], num[17], cnt[1 << 16];
    void up(int &x, int y) {if(x < y) x = y;}
    void work(int cc) {
    	n = read();
    	for(int i = 0; i < n; i++) vis[i] = -1;
    	for(int i = 0; i < n; i++) num[i] = read(), pos[i] = read() + 1;
    	for(int j = 0; j < 1 << n; j++) for(int k = 0; k <= n; k++) f[j][k] = inf;
    	for(int i = 0; i < n; i++) if(!pos[i] || pos[i] == 1) f[1 << i][i] = 0;
    	for(int s = 0, t; s < (1 << n); s++, t = cnt[s] + 1) {
    		for(int j = 0; j < n; j++) if(f[s][j] > inf){
    			for(int k = 0; k < n; k++) {
    				if(j == k || s & (1 << k) || (pos[k] && pos[k] != t)) continue;
    				up(f[s | (1 << k)][k], f[s][j] + num[j] * num[k]);
    			}
    		}
    	}
    	int ans = inf;
    	for(int i = 0; i < n; i++) ans = max(ans, f[(1 << n) - 1][i]);
    	printf("Case #%lld:
    %lld
    ", cc, ans);	
    }
    signed main()
    {
    	for(int i = 1; i < 1 << 16; i++) 
    		cnt[i] = cnt[i >> 1] + (i & 1);
    	int Case = read(), cc = 0;
    	while(Case--) work(++cc);	
    	return 0;
    }
    /*
    f[i][j]表示前j个位置,坐法为i的最大大小是多少
    */
    
  • 相关阅读:
    JavaScript父子页面之间的相互调用
    把一个集合自定转成json字符串
    分页类与前台和后台的调用方法
    移动App测试实战—专项测试
    功能测试需求分析方法
    App弱网测试
    App常见产品问题及预防方法
    21天,搞定软件测试从业者必备的Linux命令
    Docker入门
    Linux核心-慧测课堂笔记
  • 原文地址:https://www.cnblogs.com/onglublog/p/14383934.html
Copyright © 2020-2023  润新知