1. 程式人生 > 其它 >A. Acing the contest 狀壓dp 揹包

A. Acing the contest 狀壓dp 揹包

技術標籤:動態規劃題解dp動態規劃SYSU狀壓題解

A common programming contest format includes teams with three contestants working to solve the maximum amount of programming challenges in the less amount of time using a single computer. Some contests have been created using a similar idea. In this contest a team has T team members, each team member has an energy Ei. The higher the energy of the member the more and the harder problems he can solve. There are a total of P programming challenges, to be solved, using a single computer. The programming challenges are numbered with numbers from 1 to P, each one having a difficulty of Di and a score value of Si.

The contest runs as follows, the first team member will sit in front of the computer and then the contest will start , then the first problem will be shown to him, he has to decide either to solve the problem, to do not solve it and go try the next one, or to stop solving problems. A team member can solve a problem if and only if it’s remaining energy is grater or equal to the problem difficulty. Once the team member decides to stop solving problems, he can not solve problems again and the next team member will sit in front of the computer and start with the problem the previous team member left. If the team member i solves problem j, then it will take Dj from his Ei energy and score Sj points to the team. If a team member decides to not solve a problem and go to the next one, no other member in the team can go back to solve it. The contest ends for the team when either there are no more problems to solve, or there are no team members left to work on the remaining problems.

For this particular problem your task is, given the energy of each team member, the difficulty and score of the problems in a contest, determine in what order the team members should solve the problems so the team can score their maximum possible score.

Input
The first line of input contains two integer numbers separated by a space T and P (1≤T≤10, 1≤P≤100), representing, respectively, the number of team members in the team, and the number of problems in the contest. The next line contains T integer numbers separated by a space, where the i-th number represents the energy Ei (1≤Ei≤100) of the team member. The next line contains P integer numbers separated by a space, where the i-th number represents the difficulty Di (1≤Di≤100) of the i-th problem in the contests, the next and last line of input contains P integer number separated by a space, where the i-th number represents the score Si (1≤Si≤100) the i-th problem gives to the team if they solve it.

Output
Output a line with a single integer number, the maximum possible score the team can obtain.

Example
inputCopy
3 3
4 2 5
2 2 5
3 2 1
outputCopy
6

題意:給你t個隊員,p個問題,每個問題有需要的能量和貢獻,每個隊員有自己的能量值,現在讓你可以隨意給隊員排序,讓他們按順序A題,前面的人不寫的題後面的人也寫不了,問你最大貢獻。

思路:設dp[i][j]為i所表示的二進位制集合中,寫到第j題的最大貢獻。其中i就是一個表示有哪幾個人上機寫題的集合,如011,就是第三個人沒寫題,第一個和第二個人寫了題的集合表示。
那麼狀態轉移方程如何確定呢?對於dp[i][j]狀態,他的下一個狀態就是i集合裡面,找到一個為0的位,把它置為1(相當於這個人現在上機寫題了),從第j題開始寫到第k題(k從j到p)。所以狀態轉移方程就可以看成是:
d p [ n e x t S t a t e ] [ k ] = m a x ( d p [ n e x t S t a t e ] [ k ] , d p [ i ] [ j ] + 這 一 位 的 人 從 第 j 題 開 始 寫 到 第 k 題 最 優 解 A n s dp[nextState][k] = max(dp[nextState][k], dp[i][j] + 這一位的人從第j題開始寫到第k題最優解Ans dp[nextState][k]=max(dp[nextState][k],dp[i][j]+jkAns)
而Ans怎麼求?思考一下便發現其實這就是一個普通的01揹包問題,只是不是從第一個物品開始選,而是從第j個物品開始選。這一步我們可以先預處理完成。

思路大概簡述這麼多,主要還是看程式碼註釋吧

AC程式碼:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define endl '\n'
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll T[15];
ll n;

typedef struct Pro
{
    ll D;
    ll S;
}P;

P a[105];

int dp[1<<10][102]; //dp[i][j][k] 表示i集合表示的人中,寫到第j題的最優解
int dp1[102][102][102];  //dp1[i][j][k] 表示從第i題開始,做到第j個問題時,能量還剩k點時的最優解
int ok[1<<10][102];

int main()
{
    ll t = read(), p = read();
    rep(i,1,t) T[i] = read();
    rep(i,1,p) a[i].D = read();
    rep(i,1,p) a[i].S = read();

    rep(s,1,p)  //先預處理得到第s題開始,做到第j個問題時,能量還剩k點時的最優解
    {
        rep(k,1,100) rep(j,s,p)
        {
            dp1[s][j][k] = max(dp1[s][j][k-1],dp1[s][j-1][k]);
            if(k>=a[j].D&&dp1[s][j-1][k-a[j].D]+a[j].S>dp1[s][j][k])
            dp1[s][j][k] = dp1[s][j-1][k-a[j].D]+a[j].S;
        }
    }

   ll ma = 0;
    rep(i,0,(1<<t)-1) rep(j,1,p)        //核心程式碼,對於dp[i][j],
    {
        rep(last,1,t) if(!((i>>(last-1))&1))    //可以找到一個還沒加入集合的人,讓他來上機寫題
        {
            int nextState = i + (1<<(last-1));  //下一個狀態就是把他加上
            int s = ok[i][j]?j+1:j;     //開始位置,取決於j這個位置有沒有被選上
            rep(k,s,p)      //看看當前這個人從第j題出發寫寫到第k題最優解是什麼
            {
                if(dp[nextState][k] < dp1[s][k][T[last]] + dp[i][j])
                {
                    dp[nextState][k] = dp1[s][k][T[last]] + dp[i][j];
                    ok[nextState][k] = 1;
                    ma = max(ma,dp[nextState][k]);
                }
            }
        }
    }
    cout<<ma<<endl;
    return 0;
}