枚举子结构得到最优解的动态规划问题

当子问题的数量不多时,通常我们能够比较清晰地求出最优解的结构,然后理清各种状态之间转移的过程。但是,如果一个动态规划拥有多个子结构时,我们往往会觉得无从下手,面对这种情况,我们可以考虑下枚举子结构,然后得到动态规划的最优解。而且,有时候我们在枚举子结构时,还要运用另外一些最优结构。我们看看下面几个例子。

1.hdoj  1584 蜘蛛牌

我们定义dp[i][j]表示从牌的大小为i到牌的大小为j这一串牌,通过移动得到满足条件的一堆牌的最小步数。对于牌1来说,他必须移到到2的上面,但是我们不知道,当他移到2位置上时2到底在哪,所以我们可以枚举2的位置。这样我们就得到了状态转移方程:dp[1][10] = dp[2][i] + dp[i][10] + dis[1][i] ; (2<=j<=10, dis[i][j]表示牌i和牌j之间的距离)。这样我们就用子问题的最优解构造出了原问题的最优解了。接下来我们可以利用子问题的最优解来递归定义问题的最优解。当然我们可以用递推来实现。这样问题便解决了。

递归实现:

#include <iostream>
#include <cmath>
using namespace std;


int num[11], dis[11][11];

void init() {
    int i, j, a;
    for( i=1; i<=10; ++i ) {
        scanf( "%d", &a );
        num[a] = i;
    }
    for( i=1; i<=10; ++i ) {
        for( j=1; j<=10; ++j ) {
            dis[i][j] = abs( num[i] - num[j] );
        }
    }
}



int solve(int l, int r ) {
    int i, s = 99999;
    if( l == r ) return 0;
    if( r - l == 1 ) return dis[l][r];
    for( i=l+1; i<=r; ++i ) {
        int tp = solve( l+1, i ) + solve(i, r) + dis[l][i];
        if( tp < s ) s = tp;
    }
    return s;
}




int main() {
//    freopen( "c:/aaa.txt", "r", stdin );
    int T;
    while( scanf( "%d", &T ) == 1 ) {
        while( T-- ) {
            init();
            printf( "%d\n", solve(1, 10));
        }
    }
    return 0;
}

 递推实现:

#include <iostream>
#include <cmath>
using namespace std;


char num[11], dis[11][11], dp[11][11];

void init() {
    int i, j, a;
    for( i=1; i<=10; ++i ) {
        scanf( "%d", &a );
        num[a] = i;
    }
    for( i=1; i<=10; ++i ) {
        for( j=1; j<=10; ++j ) {
            dp[i][j] = 0;
            dis[i][j] = abs(num[i] - num[j] );
        }
        dp[i][i+1] = dis[i][i+1];
    }
}



char solve() {
    int i, j, k, len;
    for( len=3; len<=10; ++len ) {
        for( i=1; i<=11-len; ++i ) {
            dp[i][i+len-1] = 999999;
            for( j=i+1; j<=i+len-1; ++j ) {
                if( dp[i+1][j] + dp[j][i+len-1] + dis[i][j] < dp[i][i+len-1]) dp[i][i+len-1] = dp[i+1][j] + dp[j][i+len-1] + dis[i][j];
            }
        }
    }
    return dp[1][10];
    
}
    

int main() {
//    freopen( "c:/aaa.txt", "r", stdin );
    char T;
    while( scanf( "%d", &T ) == 1 ) {
        while( T-- ) {
            init();
            printf( "%d\n", solve());
        }
    }
    return 0;
}

2.hdoj 1227 Fast Food

横坐标上有n个点,然后选m个点,使得n个点中每个点离所选的点的最近距离的和的最小值。

当我们拿到题目,试着寻找问题最优解的子结构时,我们可以发现当我们放m个点的最后一点时,他有很多种情况。所以,接下来我们需要对所有的情况进行枚举。在放最后一个的时候,前m-1个已经放了,所以最后一个可能放在第m个位置到最后一个位置的所有位置。而这些状态会产生一个最优解。所以我们可以定义dp[i][j]为从1到i已经放了j个的最优解,则dp[n][m] = dp[i][m-1] + dis[i+1][n]; ( m-1<=i<=n-1,dis[i][j]表示点i到点j之间放一点,所得到的最小值)

#include <iostream>
#include <cmath>
using namespace std;

int n, m, num[205], dis[205][205], dp[202][35];

void input() {
	for( int i=0; i<n; ++i ) scanf( "%d", &num[i] );
}


void getDis() {
	int i, j, k, mid;
	memset( dis, 0, sizeof(dis) );
	for( i=0; i<n; ++i ) {
		for( j=i+1; j<n; ++j ) {
			mid = (i+j) / 2;
			for( k=i; k<=j; ++k ) {
				dis[i][j] += abs(num[k] - num[mid]);
			}
		}
	}
}


void solve() {
	int i, j, k;
	memset( dp, 0, sizeof( dp ));
	for( i=0; i<n; ++i ) dp[i][1] = dis[0][i];
	for( i=2; i<=m; ++i ) {
		for( j=i-1; j<n; ++j ) {
			dp[j][i] = 9999999;
			for( k=i-2; k<=j-1; ++k ) {
				if( dp[k][i-1] + dis[k+1][j] < dp[j][i] ) dp[j][i] = dp[k][i-1] + dis[k+1][j];
			}
		}
	}
	printf( "Total distance sum = %d\n\n", dp[n-1][m]);
}


int main() {
//	freopen( "c:/aaa.txt", "r", stdin );
	int ca = 1;
	while( scanf("%d %d", &n, &m), n+m ) {
		printf( "Chain %d\n", ca++ );
		input();
		getDis();
		solve();
	}
	return 0;
}

3.hdoj 1244 Max Sum Plus Plus Plus

#include <iostream>
using namespace std;

int n, m, num[1005], len[25], lensum[25], sum[1005];
int dp[1002][22];

void init() {
    int i;
    scanf( "%d", &m );
    lensum[0] = 0;
    for( i=1; i<=m; ++i ) {
        scanf( "%d", &len[i] );
        lensum[i] = lensum[i-1] + len[i];
    }
    sum[0] = 0;
    for( i=1; i<=n; ++i ) {
        scanf( "%d", &num[i] );
        sum[i] = sum[i-1] + num[i];
    }
}

void solve() {
    int i, j, k, maxx;
    memset( dp, 0, sizeof(dp) );
    for( i=1; i<=n; ++i ) {
        for( j=1; j<=m; ++j ) {
            if( lensum[j-1] > i ) break;
            if( i+len[j] > n ) continue;
            maxx = 0;
            for( k=lensum[j-1]; k<=i; ++k ) if( dp[k][j-1] > maxx ) maxx = dp[k][j-1];
            dp[i+len[j]][j] = maxx + sum[i+len[j]] - sum[i];
        }
    }
    maxx = 0;
    for( i=1; i<=n; ++i ) if( dp[i][m] > maxx ) maxx = dp[i][m];
    printf( "%d\n", maxx );
}


int main() {
//    freopen("c:/aaa.txt", "r", stdin );
    while( scanf("%d", &n) == 1 && n ) {
        init();
        solve();
    }
    return 0;
}

.

posted on 2011-03-26 19:45  CrazyAC  阅读(769)  评论(0编辑  收藏  举报