UVa12105越大越好

题意

在这里插入图片描述

思路:

  1. 第一个思路很容易想到,dp[i][j]代表用了i根火柴,除m余j的最大数。用刷表法,每次选择一个数k放在最右边,用dp[i][j]×10+k去更新dp[i+needs[k]][ ( j×10+k) % m], 其中needs[i]:i 所需要的火柴个数。不过因为有100个火柴,会涉及到大数。导致计算量挺大,890ms。
  2. 第二个思路不好想。定义状态(i,j)表示用不超过i根火柴拼出“除以m余数为j”的整数的最大长度。设dp(i,j)。p(i,j)记录满足状态(i,j)的整数的最高位数字。
    转移方程:dp(i,j) = max{ dp(i-needs[k] , (j * 10+k)%m)+ } ( i >= needs[k] )。
    上述方程表示当我们从高位往低位写数字的时候,假设当前写的数字是x,高位的数字除以m的余数为j,那么把x添加到前面写过的数字后面时候,相当于先对前一次写出来的数10,然后加上x,那么这个新数的余数就是(j10+x)%m。由于dp值表示的位数,因此我们取位数最大的那个dp值,然后+1,就是状态(i,j)的最大长度,同时更新状态(i,j)对应的那个数字x,又因为我们希望写出的数尽可能的大,因此枚举x的h时候要从大到小枚举,而且必须满足c[x]<=i,其中c[x]表示写出数字x需要使用的火柴数。100ms.
1.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 105;
const int maxm = 3005;

int n, m;
char d[maxn][maxm][52];
int needs[] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };

bool bigger(char *s1, char *s2){
	int len1 = strlen(s1), len2 = strlen(s2);
    if(len1 > len2) return true;
    if(len1 < len2) return false;
    for(int i = 0; i <= len1; ++i){
        if(s1[i] > s2[i]) return true;
        if(s1[i] < s2[i]) return false;
    }
    return false;
}

void update(char *s1, char *s2, int k){
	char t[52];
	strcpy(t,s1);
	int len = strlen(t);
	if(t[0] == '0') t[0] = k + '0';
	else { 	
		t[len] = '0' + k; 
		t[len+1] = '\0';
	}

	if(bigger(t, s2)) strcpy(s2, t);
}

int main()
{
	freopen("in.txt","r",stdin);
	int kase = 0;
	while(scanf("%d %d",&n,&m) == 2&&n){
		printf("Case %d: ", ++kase);
		
		for(int i = 0; i <= n; i++)
            for(int j = 0; j < m; ++j) 
				d[i][j][0] = '\0';
		d[0][0][0] = '0'; d[0][0][1] = '\0';
		
		for(int i = 0; i <= n; ++i){ // 火柴数 
			for(int j = 0; j < m; ++j){ if(strlen(d[i][j]) > 0)
			for(int k = 0; k <= 9; ++k)
				if(i+needs[k] <= n)
				update(d[i][j], d[i+needs[k]][(j*10+k)%m], k);
			}
		}
		
		int ans = -1;
		for(int i = 2; i <= n; ++i){
			if(strlen(d[i][0]) > 0){
				if(ans == -1 || bigger(d[i][0], d[ans][0]))
					ans = i;
			}
		}
		if(ans == -1) printf("-1");
		else printf("%s", d[ans][0]);
		printf("\n");
	}

	return 0;
}

2.
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int maxn = 105;
const int maxm = 3005;

// dp[i][j] is the maximal length of the integer whose remainder is j (with at most i matches)
// p[i][j] is the maximal digit for state (i,j), p[i][j]表示这个数首位是什么
int n, m, dp[maxn][maxm], p[maxn][maxm];

int needs[] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };

int main(){
	int kase = 0;
	freopen("in.txt","r",stdin); 
	while(scanf("%d%d", &n, &m) == 2) {
	    printf("Case %d: ", ++kase);
	
	    for(int i = 0; i <= n; i++) 
			for(int j = 0; j < m; j++){     
				int& ans = dp[i][j];
				ans = p[i][j] = -1;
				if (j == 0) ans = 0;
				for(int d = 9; d >= 0; d--) if (i >= needs[d]){
					int t = dp[i - needs[d]][(j * 10 + d) % m];
					if (t >= 0 && t + 1 > ans){   //t exist
						ans = t + 1;
						p[i][j] = d;
					}
				}
	    	}
	
	    if (p[n][0] < 0) printf("-1");
	    else {
			int i = n, j = 0;
		    for(int d = p[i][j]; d >= 0; d = p[i][j]){
				printf("%d", d);
				i -= needs[d];
				j = (j * 10 + d) % m;
				//printf(" j = %d", j);
		    }
	    }
	    printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CY05627/article/details/88748079