알고리즘 디자인 과 분석 제4 장 동적 계획 (1) [가방 문제]

제3 장 동적 계획 (1) [가방 문제]
기본 사상: 동적 계획 알고리즘 은 분 치 법 과 유사 하 다. 그 기본 사상 도 해결 해 야 할 문 제 를 몇 개의 키 문제 로 분해 하지만 분 해 를 통 해 얻 은 서브 문 제 는 서로 독립 된 것 이 아니다.서로 다른 하위 문제 의 수 는 항상 다항식 급 만 있다.분 치 법 으로 해 를 구 할 때, 어떤 문 제 는 여러 차례 중복 계산 되 었 다.해 결 된 하위 문제 의 답 을 저장 하고 필요 할 때 구 한 답 을 찾 으 면 중복 계산 을 피하 고 여러 가지 시간 알고리즘 을 얻 을 수 있다.
기본 요소:
1. 최 우수 서브 구조 적 문제 의 최 적 화 는 그 서브 문제 의 최 적 화 를 포함한다.이런 성질 을 최 우선 자 구조 성질 이 라 고 한다.문제 의 가장 좋 은 서브 구조 적 성질 을 분석 할 때 사용 하 는 방법 은 보편성 을 가진다. 먼저 문제 의 가장 좋 은 해석 으로 도 출 된 서브 문제 의 해석 이 가장 좋 은 것 이 아니 라 고 가정 한 다음 에 이 가설 에서 원래 의 문제 보다 가장 좋 은 해석 을 구 조 했 고 창 방 패 를 만 들 수 있다 는 것 을 설명 한다.문제 의 최 우선 서브 구조 성질 을 이용 하여 밑 에서 위로 돌아 가 는 방식 으로 서브 문제 의 최 우선 에서 점차적으로 전체 문제 의 최 우선 을 구성한다.가장 좋 은 서브 구 조 는 문 제 를 동적 계획 알고리즘 으로 풀 수 있 는 전제 이다.
2. 중첩 서브 문제 의 성질 재 귀 알고리즘 이 문 제 를 풀 때 매번 발생 하 는 서브 문 제 는 항상 새로운 문제 가 아니 라 일부 서브 문 제 는 여러 번 반복 적 으로 계산 된다.이런 성질 을 자 문제 의 중첩 성 이 라 고 한다.동적 계획 알고리즘 은 모든 하위 문 제 를 한 번 만 풀 고 한 표 에 저장 합 니 다. 이 문 제 를 다시 풀 어야 할 때 상수 시간 으로 결 과 를 간단하게 볼 수 있 습 니 다.일반적으로 서로 다른 하위 문제 의 개 수 는 문제 의 크기 에 따라 다항식 으로 증가한다.따라서 동적 기획 알고리즘 을 사용 하면 여러 가지 시간 만 있 으 면 비교적 높 은 문제 풀이 효율 을 얻 을 수 있다.
3. 비망록 방법 비망록 방법의 통제 구 조 는 직접 재 귀 방법의 통제 구조 와 같 고 비망록 방법 은 모든 해 제 된 서브 문제 에 비망록 을 만들어 필요 할 때 조회 하고 같은 서브 문제 의 중복 구 해 를 피 하 는 것 과 구별 된다.
기본 절차: 1. 가장 좋 은 성질 을 찾아내 고 그 구조 적 특징 을 묘사한다.2. 최 적 치 를 재 귀적 으로 정의 한다.3. 아래 에서 위로 가장 좋 은 값 을 계산한다.4. 최 우수 치 를 계산 할 때 얻 은 정보 에 따라 구조 가 가장 좋다.
3.1 가방 문제 (문제 분석 은 HTML Help)
가방 문제 (Knapsack problem) 는 조합 이 최 적 화 된 NP 완전 문제 다.
1.01 가방 문제
N 개의 아 이 템 과 V 용량 의 가방 이 있 습 니 다.제 i 아 이 템 의 무 게 는 wi [i] 이 고 가 치 는 val [i] 입 니 다.어떤 물건 을 가방 에 넣 으 면 이 물건 들 의 무 게 를 합계 하여 가방 용량 을 초과 하지 않 고 가치 총화 가 가장 큽 니까?
가장 기본 적 인 가방 문 제 는 각 아 이 템 이 하나 밖 에 없어 놓 거나 놓 지 않 는 것 이 특징 이다.
하위 문제 로 상 태 를 정의 합 니 다: 즉 DP [i] [v] 는 앞의 i 개 아 이 템 을 v 용량 의 가방 에 넣 으 면 얻 을 수 있 는 최대 가 치 를 표시 합 니 다.그 상태 전이 방정식 은:
DP[i][v]=max{ DP[i-1][v], DP[i-1][v-wi[i]]+val[i] }。
(공간 압축 가능, DP [v] = max {DP [v], DP [v - wi [i]]] + val [i]})
방정식 은 "앞의 i 개 아 이 템 을 v 용량 의 가방 에 넣 는 것" 이 라 고 설명 했다. 이 문 제 는 i 개 아 이 템 의 전략 (넣 거나 놓 지 않 음) 만 을 고려 하면 앞의 i - 1 개 아 이 템 과 관련 된 문제 로 바 뀔 수 있다. 만약 에 i 개 아 이 템 을 넣 지 않 으 면 문 제 는 '앞의 i - 1 개 아 이 템 을 v 용량 의 가방 에 넣 는 것' 으로 바 뀌 고 가 치 는 DP [i - 1] [v] 이다.i 번 째 아 이 템 을 넣 으 면 문 제 는 '앞의 i - 1 아 이 템 을 남 은 용량 의 v - wi [i] 가방 에 넣 기' 로 바 뀌 는데 이때 얻 을 수 있 는 가장 큰 가 치 는 DP [i - 1] [v - wi] 인 데다 가 i 번 째 아 이 템 을 넣 어 얻 을 수 있 는 가치 val [i] 이다.
공간 복잡 도 최적화
DP [i] [v] 는 DP [i - 1] [v] 와 DP [i - 1] [v - wi [i] 두 가지 문제 가 밀 려 왔 습 니 다. DP [i] [v] 를 밀 때 (즉, 첫 번 째 메 인 순환 에서 DP [v] 를 밀 때) DP [i - 1] [v] 와 DP [i - 1] [v - wi]] 의 값 을 얻 을 수 있 습 니까?사실, 이것 은 매번 메 인 순환 에서 우 리 는 v = V... 0 의 순서 로 DP [v] 를 밀어 야 DP [v] 를 밀 때 DP [v - wi] 가 상태 DP [i - 1] [v - wi] 의 값 을 저장 할 수 있 습 니 다.의사 코드 는 다음 과 같 습 니 다: for i = 1... N for v = V... 0 DP [v] = max {DP [v], DP [v - wi [i]]] + val [i]};
알고리즘 복잡 도: 시간 복잡 도: O (VN), 압축 공간 후 공간 복잡 도 O (V)
코드:
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int DP[1010],wi[1010],val[1010];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
		int i,j,n,v;
        //         
		//(1)        ,    :DP[0]=0,DP[1..v]=-∞,            DP[N]             。
        //(2)           ,        ,    :DP[0..v]=0。
        memset(DP,0,sizeof(DP));
        memset(wi,0,sizeof(wi));
        memset(val,0,sizeof(val));
        scanf("%d%d",&n,&v);
        for(i=1; i<=n; i++)
        {
            scanf("%d",&val[i]);
        }
        for(i=1; i<=n; i++)
        {
            scanf("%d",&wi[i]);
        }
        for(i=1; i<=n; i++)
        {
            for(j=v; j>=wi[i]; j--)
            {
                DP[j]=max(DP[j],DP[j-wi[i]]+val[i]);
            }
        }
        for(i=1; i<=v; i++)
        {
            printf("%d ",DP[i]);
        }
        printf("
"
); } return 0; }

2. 완전 가방 은 N 가지 아 이 템 과 V 용량 의 가방 이 있 으 며, 모든 아 이 템 은 무한 정 사용 가능 합 니 다.제 i 종 물품 의 비용 은 c [i] 이 고 가 치 는 w [i] 입 니 다.어떤 물건 을 가방 에 넣 으 면 이 물건 들 의 비용 을 합 쳐 가방 용량 을 초과 하지 않 고 가치 가 가장 큰 지 알 아 보 세 요.
1 차원 배열, 위조 코드 사용: for i = 1... N for v = 0... V DP [v] = max {DP [v], DP [v - cost] + weight}
분석: 01 가방 의 위조 코드 와 v 의 순환 순서 만 다르다.01 가방 에서 v = V... 0 의 역순 으로 순환 하 는 것 은 i 차 순환 중의 상태 DP [i] [v] 가 상태 DP [i - 1] [v - val [i]] 에서 전달 되 기 때 문 입 니 다.다시 말 하면 이것 은 모든 아 이 템 을 한 번 만 선택 할 수 있 도록 하기 위해 서 이다. 'i 번 째 아 이 템 선택' 전략 을 고려 할 때 i 번 째 아 이 템 에 선택 한 하위 결과 DP [i - 1] [v - wi [i]] 를 근거 로 한다.현재 완전 가방 의 특징 은 바로 모든 아 이 템 이 무한 개 를 선택 할 수 있 기 때문에 'i 번 째 아 이 템 추가 선택' 이라는 전략 을 고려 할 때 i 번 째 아 이 템 에 선 택 된 하위 결과 DP [i] [v - wi [i]] 가 필요 하기 때문에 v = 0... V 의 순서 로 순환 해 야 합 니 다.
[hdu 1114] 코드:
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3fffffff
int p[10010],w[10010],dp[10010];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int e,f,v,i,j,n;
        scanf("%d%d",&e,&f);
        v=f-e;
        scanf("%d",&n);
        for(i=1; i<=n; i++)
        {
            scanf("%d%d",&p[i],&w[i]);
        }
        for(i=1; i<=v; i++)
        {
            dp[i]=INF;
        }
        for(i=1; i<=n; i++)
        {
            for(j=w[i]; j<=v; j++)
            {
                dp[j]=min(dp[j],dp[j-w[i]]+p[i]);
            }
        }
       /* for(j=1; j<=v; j++)
        {
            printf("%d ",dp[j]);
        }
        printf("
");*/
if(dp[v]==INF) { printf("This is impossible.
"
); } else { printf("The minimum amount of money in the piggy-bank is %d.
"
,dp[v]); } } return 0; }

3. 다 중 가방 문제
N 가지 아 이 템 과 V 용량 의 가방 이 있 습 니 다.제 i 종 아 이 템 은 최대 n [i] 개 를 사용 할 수 있 습 니 다. 모든 비용 은 c [i] 이 고 가 치 는 w [i] 입 니 다.어떤 물건 을 가방 에 넣 으 면 이 물건 들 의 비용 을 합 쳐 가방 용량 을 초과 하지 않 고 가치 가 가장 큰 지 알 아 보 세 요.
01 가방 으로 전환 가능 구 해: 제 i 종 아 이 템 을 n [i] 건 01 가방 중의 아 이 템 으로 교환 하면 아 이 템 수 를 획득Σn [i] 의 01 가방 문 제 를 직접 해결 하고 복잡 도 는 여전히 O (V *Σn[i])。
방법: i 번 째 물품 을 여러 가지 물품 으로 나 누 는데 그 중에서 모든 물품 은 하나의 계수 가 있 는데 이 물품 의 비용 과 가 치 는 모두 원래 의 비용 과 가 치 를 이 계수 에 곱 한다.이러한 계 수 를 각각 1, 2, 4,..., 2 (k - 1), n [i] - 2k + 1 로 하고 k 는 n [i] - 2 ^ k + 1 > 0 을 만족 시 키 는 최대 정수 이다.예 를 들 어 n [i] 가 13 이면 이 물건 을 계수 로 나 누 어 각각 1, 2, 4, 6 의 네 가지 물건 으로 나눈다.
알고리즘 복잡 도: i 종 아 이 템 을 O (log n [i]) 종 아 이 템 으로 나 누 어 원래 의 문 제 를 복잡 도로 O (V * 로 전환시킨다.Σ질문코드:
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int dp[200];
void CompletePack(int cost, int weight,int n)
{
    int i;
    for(i=cost; i<=n; i++)
        dp[i]=max(dp[i],dp[i-cost]+weight);
}
void Zero_OnePack(int cost,int weight,int n)
{
    int i;
    for(i=n; i>=cost; i--)
        dp[i]=max(dp[i],dp[i-cost]+weight);
}
int main()
{
    int cost[200],weight[200],amount[200];
    int i,j,k;
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=0; i<m; i++)
            scanf("%d%d%d",&cost[i],&weight[i],&amount[i]);
        memset(dp,0,sizeof(dp));
        for(i=0; i<m; i++)
        {
            if(cost[i]*amount[i]>n) CompletePack(cost[i],weight[i],n);
            else
            {
                k=1;
                while(k<amount[i])
                {
                    Zero_OnePack(k*cost[i],k*weight[i],n);
                    amount[i]-=k;
                    k*=2;
                }
                Zero_OnePack(amount[i]*cost[i],amount[i]*weight[i],n);
            }
        }
        printf("%d
"
,dp[n]); } return 0; }

좋은 웹페이지 즐겨찾기