uva242, 完全背包||记忆化搜索,(类似广工校赛的一道题)




模仿自:http://blog.csdn.net/squee_spoon/article/details/38014425#

(下面说的都是假的)

自己明明看过别人的代码的,结果写的代码还是TLE,因为我是对于每一个邮票集合都求,,这样的话可能会出现很大的情况。。因为10*200*

然后别人的题解是根据能不能到达某个价值来求。。。而且有一个更坑的是,我把别人的代码的int数组改为bool数组就会超时。。。(我明明记得又一次如果用的在函数中如果用的是bool函数反而会快,用int返回的函数反而超时。。。


(上面说的都是假的。。。)


其实我的TLE 的原因和改他的TLE d原因只是因为  d!=-1表示没有计算过,其他表示计算过,所以就记忆化。。



这是记忆化的做法:


我的

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<stack>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
#define INF 1000000000
#define bug1 cout<<"bug1"<<endl;
#define bug2 cout<<"bug2"<<endl;
#define bug3 cout<<"bug3"<<endl;
using namespace std;
int s,set;
int stamp[12][12];
int d[1110][12];
int dp(int kind,int n,int s){
    if(d[n][s]!=-1)return d[n][s];
    if(!n)return d[n][s]=1;
    if(!s)return d[n][s]=0;
    for(int i=1;i<=stamp[kind][0];++i){
        if(n-stamp[kind][i]>=0)
            if(dp(kind,n-stamp[kind][i],s-1))return d[n][s]=1;
    }
    return d[n][s]=0;
}
int maxans[12];
int ansid,ans;
void getans(int st,int s){
    memset(d,-1,sizeof(d));
    for(int i=1;i<inf;++i){
        if(!dp(st,i,s)){maxans[st]=i-1;return;}
    }
}
int solve(){
    for(int i=1;i<=set;++i){getans(i,s);}
    for(int i=1;i<=set;++i){
        if(ans<maxans[i]){
            ans=maxans[i];
            ansid=i;
        }
        else if(ans==maxans[i]){
            if(stamp[ansid][0]<stamp[i][0]){
                ans=maxans[i];
                ansid=i;
            }
            else if(stamp[ansid][0]==stamp[i][0]){
                for(int j=stamp[ansid][0];j>=1;j--){
                    if(stamp[ansid][j]<stamp[i][j]){
                        ans=maxans[i];
                        ansid=i;
                        break;
                    }
                }
            }
        }
    }
    return ans;
}
int main(){
    while(~scanf("%d",&s)&&s){
        scanf("%d",&set);
        for(int i=1;i<=set;++i){
            scanf("%d",&stamp[i][0]);
            for(int j=1;j<=stamp[i][0];++j){
                scanf("%d",&stamp[i][j]);
            }
        }
        printf("max coverage = %4d:",solve());
    }
}

还有一个用完全背包的做法。。感觉非常好。。

转自http://www.cnblogs.com/zyb993963526/p/6380042.html


#include<iostream> 
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 25;
const int INF = 0x3f3f3f3f;

int s, n, m;
int a[maxn];
int dp[1005];
int ans[25];


int main()
{
    //freopen("D:\\txt.txt", "r", stdin);
    while (cin >> s && s)
    {
        int best = 0;         //最大连续邮资
        int Max=INF;          //最大邮票的值
        int number = INF;     //邮票数量
        cin >> n;
        for (int i = 0; i < n; i++)
        {
            cin >> a[0];
            for (int j = 1; j <= a[0]; j++)
                cin >> a[j];
            memset(dp, INF, sizeof(dp));
            dp[0] = 0;
            int now = 0;
            for (int j = 1; j <= s*a[a[0]]+1; j++)
            {
                for (int k = 1; k <= a[0] && j >= a[k]; k++)
                    dp[j] = min(dp[j], dp[j - a[k]] + 1);
                if (dp[j]>s)
                {
                    now = j - 1;
                    break;
                }
            }
            if (now > best)   //此时的最大连续邮资大于了之前的
            {
                best = now;
                number = a[0];
                Max = a[a[0]];
                memcpy(ans, a, sizeof(a));
            }
            else if (now == best)   //如果相等时
            {
                if (a[0] < number)   //首先考虑邮票数量少的
                {
                    number = a[0];
                    Max = a[a[0]];
                    memcpy(ans, a, sizeof(a));
                }
                else if (a[a[0]] < Max)   //如果邮票数量一样多,则优先考虑邮票最大的那张更小的
                {
                    Max = a[a[0]];
                    memcpy(ans, a, sizeof(a));
                }
            }
        }
        printf("max coverage =%4d :", best);
        for (int i = 1; i <= number; i++)printf("%3d", ans[i]);
        puts("");
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值