POJ 1010 STAMPS(dfs)

125 篇文章 0 订阅

Descirption
给出不同类型的一组邮票,类型面值可能相同。求符合总面值,且最多取4张邮票的最佳方案。最佳方案满足以下要求:
1.类型数最多
2.如果类型数相同,则张数少者
3.如果张数也相同,则单张面值最大者
4.如果以上都相同,则无最佳方案(平局)
Input
多组用例,每组用例占两行,第一行输入多个整数为几种邮票的面值,以0结束面值输入,第二行输入多个整数,每个整数表示一组查询的总面值,以0结束查询输入,以文件尾结束全部输入
Output
对于每次查询,如果存在最优方案则输出最优方案所选取的邮票种类数以及每张邮票的面值(升序输出),如果没有满足条件的方案则输出none,如果平局则输出tie
Sample Input
1 2 3 0
7 4 0
1 1 0
6 2 3 0
Sample Output
7 (3): 1 1 2 3
4 (2): 1 3
6 —- none
2 (2): 1 1
3 (2): tie
Solution
数据较小直接暴力枚举四张邮票就行,此处采取dfs搜索最优方案,大致做法是每找到一组合法解就与已得到的最优解进行比较更新最优解,注意标记none和tie两种情况
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int value[555],res,m;//value数组存储邮票价值,res存储邮票张数,m存储消费者所需价值 
int ans[5],ansnum;//ans存储答案值在value数组中对应的下标,ansnum表示答案所对应的邮票数 
int temp[5],cnt;//temp数组存储中间答案(同样是下标),cnt表示temp对应的邮票数 
bool tie,flag;//tie表示是否出现平局的情况,flag标记是否有解 
bool vis[555];//标记数组,用来统计邮票种类数 
int get_kind(int *s,int n)//统计邮票种类数 
{
    int ans=0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++)
        if(!vis[s[i]])
            ans++,vis[s[i]]=1;
    return ans;
}
int get_max(int *s,int n)//找出邮票中的最大价值 
{
    int ans=0;
    for(int i=0;i<n;i++)
        ans=max(ans,value[s[i]]);
    return ans;
}
void cmp()
{
    int temp_kind=get_kind(temp,cnt);
    int temp_max=get_max(temp,cnt);
    int ans_kind=get_kind(ans,ansnum);
    int ans_max=get_max(ans,ansnum);
    if(ansnum==0||temp_kind>ans_kind||temp_kind==ans_kind&&ansnum>cnt||temp_kind==ans_kind&&ansnum==cnt&&temp_max>ans_max)
    {
        //更新答案的几种情况
        //1.尚未得到一组答案
        //2.当前答案比已知答案种类数多
        //3.当前答案比已知答案达到相同种类数时所拿邮票数少 
        //4.当前答案比已知答案在拿相同邮票数并且邮票种类数相同时所拿邮票最大价值大 
        tie=0;
        ansnum=cnt;
        for(int i=0;i<cnt;i++)
            ans[i]=temp[i];
    }
    if(ansnum==cnt&&temp_kind==ans_kind&&temp_max==ans_max)//平局 
        tie=1;
}
void dfs(int pos,int sum)//pos表示当前搜索到的邮票编号,sum表示当前已经拿的邮票价值 
{
    if(sum>m)return ;//当前值已经大于目标值,不用继续搜索,剪枝 
    if(sum==m)//遇到合法解 
    {
        flag=1;//标记有解 
        cmp();//更新最优解 
    }       
    if(cnt==4)return ;//已经拿四张邮票,剪枝 
    for(int i=pos;i<res;i++) 
    {
        temp[cnt]=i;//记录中间答案 
        cnt++; 
        dfs(i,sum+value[i]);//深搜 
        cnt--;//回溯 
    }
}
int main()
{
    while(~scanf("%d",&value[0]))
    {
        res=0;
        while(value[res])
            scanf("%d",&value[++res]);
        sort(value,value+res);//对邮票价值由小到大排序 
        while(scanf("%d",&m),m)
        {
            tie=flag=0;//初始化 
            ansnum=cnt=0;
            dfs(0,0);
            if(!flag)//无解 
                printf("%d ---- none\n",m);
            else
            {
                printf("%d (%d):",m,get_kind(ans,ansnum));
                if(tie)printf(" tie\n");//平局 
                else
                {
                    sort(ans,ans+ansnum);//对最优方案所拿邮票的价值排序输出 
                    for(int i=0;i<ansnum;i++)
                        printf(" %d",value[ans[i]]);
                    printf("\n");
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值