Zoj 3375 Imperishable Night (DP_状态压缩DP)

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3375

题目大意:题目规定有三个变量,point,Iv,tv,让我们一个一个地选择n个地方种的某个地方,然后这个地方里面有两种宝物x,y可取,x宝物有xi个,可以让tv增加ai,y宝物有yi个,可以让lv增加bi。选择x,则point += lv,tv += ai,为了防止混乱带来思维复杂度地增加,我用xv代替lv,yv代替tv,这样就好看多了。那么选择x则point += xv,yv += ai,选择y,则point += yv,xv += bi;问最后获得最大point。

解题思路:这题初看上去很复杂,但其实想好几点就很简单了。1、,本次选择某个城市,xv必定增加xi*bi,yv必定增加yi*ai,由于后面选择每个地方都要执行point += yv或者point += xv,那么这增加的xv和yv对后面的选择都会有影响,我们可以在前面就考虑对后面选择的影响。 2、选择城市是一步,选择好城市后里面的宝物如何选择才能让本次的point增加最大呢?这个贪心就好,我yy了一个结论,cnt = xi * yi,maxPoint = max(cntx * ai + cnty * bi)(cntx + cnty == cnt) .这个maxPoint是选择城市后增加的,对后面的不会有影响。可以认为选择城市和选择宝物是独立,那么我们可以预处理线算出每个点maxPoint。 3、选择城市可以用状态压缩来做,选择了某些特定的城市后,已经在后面的影响考虑在内,那么dp[i]就可以表示选择了城市编号压缩后为i的最大point。考虑好这三点之后就可以用类似于TSP的转移方法进行DP.


测试数据:
InPut:
1
1 2 1 1

3
2 2 2 2
2 2 2 2
2 2 2 2

3
2 2 1 1
2 2 1 2
2 2 2 1

OutPut:
2
72
32


代码:
#include <stdio.h>
#include <string.h>
#define MAX (1<<17)
#define int64 long long
#define max(a,b) ((a)>(b)?(a):(b))

struct node {
    int64 a, x, b, y;
} arr[MAX];
int n, m, ans;
int64 totx,toty;
int64 dp[MAX], val[MAX];


void Initial() {

    memset(val, 0, sizeof (val));
    memset(dp, 0, sizeof (dp));


    for (int i = 0; i < n; ++i) {

        int64 mmax = 0,cnt = arr[i].x * arr[i].y;
        for (int j = 0; j <= cnt; ++j)
            mmax = max(mmax,arr[i].a*j+arr[i].b*(cnt-j));
        val[i] = mmax;
        //printf("i = %d %d\n",i,mmax);
    }
}
int Solve_DP() {

    int i, j, k, st, cnt;
    int64 xv, yv, po, mmax,sumx,sumy;


    for (i = 0; i < n; ++i) {

        st = (1<<i);
        xv = yv = po = 0;
        po = val[i];
        xv = arr[i].a * arr[i].x;
        yv = arr[i].b * arr[i].y;
        po += yv * (totx - arr[i].x) + xv * (toty - arr[i].y);
        dp[st] = po;
    }
    for (i = 1; i < (1 << n); ++i) {

        cnt = n,sumx= sumy = 0;
        for (j = 0; j < n; ++j)
            if (((i >> j) & 1) == 0)
                sumx += arr[j].x ,sumy += arr[j].y;

                
        for (j = 0; j < n; ++j) {
            if (((i >> j) & 1) == 0) {

                st = (i | (1 << j));
                xv = yv = po = 0;
                po = val[j];
                xv = arr[j].a * arr[j].x;
                yv = arr[j].b * arr[j].y;
                sumx -= arr[j].x ,sumy -= arr[j].y;
                po += xv * sumy + yv * sumx;
                dp[st] = max(dp[i] + po, dp[st]);
                sumx += arr[j].x ,sumy += arr[j].y;
            }
        }
    }
}


int main() {
    int i, j, k;


    while (scanf("%d", &n) != EOF) {

        totx = toty = 0;
        for (i = 0; i < n; ++i) {
         
            scanf("%lld%lld%lld%lld", &arr[i].a, &arr[i].b, &arr[i].x, &arr[i].y);
            totx += arr[i].x ,toty += arr[i].y;
        }


        Initial();
        Solve_DP();
        //for (i = 0; i < (1<<n) ; ++i)
        //    printf("i = %d val = %d\n",i,dp[i]);
        printf("%lld\n", dp[(1 << n) - 1]);
    }
}


本文ZeroClock原创,但可以转载,因为我们是兄弟。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值