问题重述:
给出n种邮票,每种邮票有自己的面值(面值可能重复)
指定m种“总面值”,对每种“总面值”,求解满足如下条件的组合以达到该“总面值”
(1) 所用邮票在n种中可以重复选取
(2) 所用邮票张数〈=4
(3) 尽量多的使用那个不同种类的邮票 Max (Stamp Types)
(4) 若有多种方案满足(3),则选取张数最小的一种方案 Min (Stamp Num)
(5) 若有多种方案满足(3)(4),则选取“最大面额”最高的一种方案。 Max(Heightest Value)
(6) 若有多种方案满足(3)(4)(5) 则输出 “tie”
题目分析:
(1) 算法定位:
从题目的条件可知,此题必须遍求所有方案以求解,因此采用搜索的方法是非常合理的,因为题目带由一定的递推性,也可以尝试动态规划的方法.
(2) 问题优化与简化
对于搜索型题目,关键的优化就是减少重复计算,本题可由3方面的简化,避免重复性计算.
(1) 对于面额相等的邮票,可以限制在1~5张,多余的可以不处理,例如这样的输入:
1 1 1 1 1 1 1 1 0 8个1
4 0
可以简化成为:
1 1 1 1 1 0 5个1
4 0
他们的解是相同的
(2) 搜索求解时约定, 后一张邮票面额>=前面张邮票的面额,即如下的6种情况:
1 2 3 2 3 1 3 12 1 3 2 3 2 1 2 1 3
只需搜索判断一种,即 1 2 3 的情况
(3) 对于给定的n种邮票,遍历4张邮票的所有可达“总面额”的解,当m种指定面额给出,只需插标输出即可,不需要重复m次类似的搜索。(因为m次的搜索中,很多是重复计算的)
(3)算法介绍
(1) 搜索方法一: (base on 史诗’s program)
主要思路:
4重循环,枚举所有可能,依据题目条件保留最优解。
For(i=1;i<=total_stamps;i++)
For(j=i;j<=total_stamps;j++)
For(k=j;k<=total_stamps;k++)
For(l=k;l<=total_stamps;l++)
{
更新最优解
}
优化:
使用优化方案(1)(2), 修改后可以加上优化方案(3)
评价:
编程复杂度低,代码少,运行时空效率高,比赛时候推荐使用
但是扩展性受限制,若题目给的是任一k张而不是4,则必须大量修改代码
(2) 搜索方法二: (base on hawking’s program)
主要思路:
递归深度搜索,遍历所有k张邮票的可达面额的解,保留每种面额的最优
解,查表输出指定面额的解,并给定k=4,即为题目要求的情况。
Void Solve(int deep)
{
更新当前方案所达面值的最优解
if (deep>4) return;
for(int s=now[deep-1];s<=total_stamp;s++)
{
now[deep]=s;
solve(deep+1);
}
}
优化:
使用优化方案(1)(2)(3)进行截枝
评价:
深度搜索的经典模板程序,推荐学习,几乎所有的搜索都可以套用的模板
编程复杂度中,运行时空效率高,通用性强一些,但代码可能稍为多一些
(3) 动态规划: (base on my program)
主要思路:
定义:int anstype[k][i][j] 表示:
用前k种邮票,选i张,达到面额为j,最多用多少种不同的类型邮票。
定义:bool tied[k][i][j] :
k,i,j 定义如上,tied 表示 anstype[k][i][j] 的最优值是否有多种方案.即同时满足题目的条件(3)(4)时是否多种
定义:bool anstied[k][i][j] :
由于 tied 限制不够强,必须多一个anstied 表示同时满足条件(3)(4)(5)是否有多种方案
递推关系:
(1)
anstype[k][i][j]=Max{anstype[k-1][i-w][j-w*value[k]]+1} 1<=w<=4, i-w>0, j-w*value[k]>=0
(2)
在anstype 的更新最大值的同时,可以判断,若某个w 使得
anstype[k][i][j]==anstype[k-1][i-w][j-w*value[k]]+1 则
tied[k][i][j]=ture;
anstype[k][i][j]<anstype[k-1][i-w][j-w*value[k]]+1 则
tied[k][i][j]=false
(3)
至于anstied 可以这样求得:
anstype[k][i][j]<anstype[k-1][i-w][j-w*value[k]]+1时
anstied[k][i][j]=tied[k-1][i-w][j-w*value[k]]
anstype[k][i][j]==anstype[k-1][i-w][j-w*value[k]]+1时
anstied=true;
主要优化:
使用优化方案(1)(2)(3)
空间优化,由于[k][i][j]只与[k-1][i][j]有关,所以只需2维数据[i][j]就可以了
这是动态规划最常用的空间优化,另外的一些是压缩存储技巧,主要的是用位运算存储,本题没有用上,不过以后有些题目会常用。
评价:
编程复杂度 中,运行时间少,可以承受大规模数据,规模可扩展,
但是空间要求量大,必须使用压缩技巧,这样编程复杂度会增大,推荐研究算法用,比赛不用
总结:
类似本题的问题很多,光stamps问题多年来就一直存在各种变种,sticks问题与本体类似,通用的算法都是搜索,练熟搜索模板是基础,简化问题、掌握剪枝技巧是解题关键。某些特殊问题可以用动态规划,但是并不是所有的都可以,例如sticks我试过是不能用动态规划的,而且比赛时候,思考会浪费时间,所以推荐比赛时用搜索。(不排除某些题目用搜索通过不了的大数据,必须用动态规划解决,所以要估计好时间空间,选用稳妥的算法)
#include <iostream.h>
#include <strstrea.h>
#include <string.h>
int stamp[1006]; // from 1 to 25, stamp[0] means none
int s[4]; // stamps sold
int n; // number of stamp types
int m; // customer request
int k; // number of stamps sold
int t; // types of stamps sold
int max; // highest single-value stamp value
int curK; // numbers of stamps sold of current best solution
int curT; // types of stamps sold of current best solution
int curM; // highest single value of current best solution
int curS[4]; // current best solution
bool curTie; // if there is tie best solutions
int temp;
bool ok(int temp) // there are no maore than 4 stamps value=temp
{
int i,j;
for (i=1,j=0;i<n;i++) if (stamp[i]==temp) j++;
return (j<5);
}
void Dodo(){
for ( s[0]=0; s[0]<n; s[0]++)
for ( s[1]=s[0]; s[1]<n; s[1]++)
for ( s[2]=s[1]; s[2]<n; s[2]++)
for ( s[3] = s[2]; s[3]<n; s[3]++) {
if ( stamp[s[0]]+stamp[s[1]]+stamp[s[2]]+stamp[s[3]] != m ) continue;
k = 0;
t = 0;
max = 0;
if ( stamp[s[0]] ) {k++; t++; if ( stamp[s[0]]>max ) max = stamp[s[0]];}
if ( stamp[s[1]] ) {k++; if ( s[1]>s[0] ) t++; if ( stamp[s[1]]>max ) max = stamp[s[1]];}
if ( stamp[s[2]] ) {k++; if ( s[2]>s[1] ) t++; if ( stamp[s[2]]>max ) max = stamp[s[2]];}
if ( stamp[s[3]] ) {k++; if ( s[3]>s[2] ) t++; if ( stamp[s[3]]>max ) max = stamp[s[3]];}
if ( t<curT ) continue;
if ( t>curT ) {
curT = t;
curK = k;
curM = max;
curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];
curTie = false;
continue;
}
if ( k>curK ) continue;
if ( k<curK ) {
curT = t;
curK = k;
curM = max;
curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];
curTie = false;
continue;
}
if ( max<curM ) continue;
if ( max>curM ){
curT = t;
curK = k;
curM = max;
curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];
curTie = false;
continue;
}
curTie = true;
}
if ( curT == -1 ) {
cout<<m<<" ---- none"<<endl;
return;
}
cout<<m<<" ("<<curT<<"):";
if ( curTie ) {
cout<<" tie"<<endl;
return;
}
for ( int i=0; i<4; i++)
if ( curS[i]>0 ) cout<<' '<<stamp[curS[i]];
cout<<endl;
}
int main() {
char strs[255];
cin.getline(strs,255);
while ( strlen(strs) ) {
istrstream in1(strs);
s[0] = 0;
n = 1;
while ( in1 ) {
in1>>temp; //modify by duoshute
if (ok(temp)) // there are no maore than 4 stamps value=temp
{
stamp[n++]=temp;
if ( stamp[n-1] == 0 ) break;
}
}
n--;
cin.getline(strs,255);
istrstream in2(strs);
while ( in2 ) {
in2>>m;
if ( m == 0 ) break;
curK = 100;
curT = -1;
curM = -1;
curTie = false;
Dodo();
}
cin.getline(strs,255);
}
return 0;
}
#include "iostream.h"
#include "string.h"
#include "stdlib.h"
#define Max 200
int a[Max];
int b[Max];
bool ask[Max];
bool asktied[5][Max];
int anst[5][Max];
char ans[4][Max][4];
bool tied[5][Max];
int i,j,k,l,n,m,max,p,q;
int temp;
bool ok(int temp) // there are no maore than 4 stamps value=temp
{
int i,j;
for (i=1,j=0;i<n;i++) if (a[i]==temp) j++;
return (j<5);
}
int compare(const void * a,const void *b) {return *(int *)a-*(int *)b;}
void work(int oldt,int newt,int oldh,int newh,int l,int j,int k,int p)
{
int s;
if (newt<oldt) return;
if (newt==oldt)
{
tied[p+k][l]=true;
if (ask[l] && newh==oldh) asktied[p+k][l]=true;
if (newh<=oldh) return;
}
else tied[p+k][l]=tied[p][j];
if (ask[l]) asktied[p+k][l]=tied[p][j];
//ok;
anst[p+k][l]=newt;
memcpy(ans[p+k-1][l],ans[p-1][j],sizeof(ans[p+k-1][l]));
for (s=0;s<k;s++) ans[p+k-1][l][p+s]=newh;
}
void main()
{
for(;;)
{
if (cin.eof()) break;
for (n=0;;)
{
if (!(cin>>temp)) break;
if (ok(temp)) // there are no maore than 4 stamps value=temp
{
a[++n]=temp;
if (a[n]==0) break;
}
}
n--;if (n<=0) break; //end of file
qsort(a,n+1,sizeof(int),compare);
memset(ask,false,sizeof(ask));
for (m=0,max=0;;)
{
cin>>b[++m];if (b[m]==0) break;if (b[m]>max) max=b[m];ask[b[m]]=true;
}
m--;
for (j=0;j<=4;j++) for (i=0;i<=max;i++) anst[j][i]=-1;
anst[0][0]=0;
memset(ans,0,sizeof(ans));
memset(tied,false,sizeof(tied));
memset(asktied,false,sizeof(asktied));
for (i=1;i<=n;i++)
{
for (j=max;j>=0;j--)
for (p=0;p<=4;p++) if (anst[p][j]>=0)
for (k=1;k<=4;k++) if (j+a[i]*k<=max) if (p+k<=4)
{
l=j+a[i]*k;
// work(oldt,newt,oldn,newn,oldh,newh,l);
work(anst[p+k][l],anst[p][j]+1,ans[p+k-1][l][p-1],a[i],l,j,k,p);
}
}
for (i=1;i<=m;i++)
{
cout<<b[i]<<' ';
l=0;p=0;
for (j=4;j>=1;j--)
{
for (k=1;k<=4;k++) if (anst[k][b[i]]==j) {l=j;p=k;break;}
if (l>0) break;
}
if (l==0) {cout<<"---- none/n";continue;}
cout<<'('<<anst[k][b[i]]<<"): ";
if (!asktied[k][b[i]])
{
for (j=1;j<=k;j++) {cout<<(int)ans[k-1][b[i]][j-1];if (j<k) cout<<' ';}
cout<<endl;
}
else cout<<"tie/n";
}
}
}
/*
by hawking@pku
*/
#include<iostream.h>
#include<search.h>
#include<memory.h>
const int MAX_STAMP_TYPES =100;
const int MAX_TOT_VALUE = 100;
const int MAX_CUSTOMER=100;
int s[MAX_STAMP_TYPES],s_size,cus[MAX_CUSTOMER],c_size,maxc,count[100];
bool custom[MAX_TOT_VALUE];
class Combine{
public:
int tot;
int ts;//types of stamps
int f;//flag 1 can 2 tie,0 can't
int s[4];//each stamps used here
int sc;//used stamps number
int max;//single maxed stamp value;
public:
bool isHave(int type){
for(int i=0;i<sc;i++)
if(s[i]==type)return true;
return false;
}
void findMax(){
max=0;
for(int i=0;i<sc;i++)
if(::s[s[i]]>max)max=::s[s[i]];
}
void addStamp(int type){
if(!isHave(type))ts++;
s[sc++]=type;
tot+=::s[type];
if(::s[type]>max)max=::s[type];
}
void removeLastStamp(int type){
sc--;
if(!isHave(type))ts--;
tot-=::s[type];
findMax();
}
}currCus[MAX_TOT_VALUE],c;
int com(const void * a,const void *b)
{
return *((int *)a)-*((int*)b);
}
void init()
{
maxc=c_size=s_size=0;
memset(custom,0,MAX_TOT_VALUE*sizeof(bool));
memset(count,0,100*sizeof(int));
}
bool read()
{
init();
int t;
cin>>t;
if(!cin)return false;
while(t!=0){
if(count[t]++<5)s[s_size++]=t; //modify by duoshute here
cin>>t;
}
qsort(s,s_size,sizeof(int),com);
cin>>t;
while(t!=0){
cus[c_size++]=t;
custom[t]=true;
if(t>maxc)maxc=t;
cin>>t;
}
return true;
}
int a[4],b[4];
bool same(int *t1,int *t2,int n)
{
memcpy(a,t1,n*sizeof(int));
memcpy(b,t2,n*sizeof(int));
/* qsort(a,n,sizeof(int),com); //omited by duoshute
qsort(b,n,sizeof(int),com); */
return memcmp(a,b,n*sizeof(int))==0;
}
void solve(int ss,int last) //int last is added by duoshute
{
if(custom[c.tot])
{
if(!currCus[c.tot].f
||c.ts>currCus[c.tot].ts
||c.ts==currCus[c.tot].ts&&c.sc<currCus[c.tot].sc
||c.ts==currCus[c.tot].ts&&c.sc==currCus[c.tot].sc&&c.max>currCus[c.tot].max)
{
currCus[c.tot]=c;
currCus[c.tot].f=1;
}
else if(c.ts==currCus[c.tot].ts&&c.sc==currCus[c.tot].sc&&c.max==currCus[c.tot].max)
{
if(!same(c.s,currCus[c.tot].s,c.sc))
currCus[c.tot].f=2;
}
}
if(c.sc>=4||c.tot>maxc)return;
for(int i=last;i<s_size;i++)
{
c.addStamp(i);
solve(ss+1,i);
c.removeLastStamp(i);
}
}
void main()
{
while(read())
{
memset(&c,0,sizeof(Combine));
memset(currCus,0,MAX_TOT_VALUE*sizeof(Combine));
solve(0,0);
for(int i=0;i<c_size;i++)
if(currCus[cus[i]].f==0)
cout<<cus[i]<<" ---- none"<<endl;
else if(currCus[cus[i]].f==2)
cout<<cus[i]<<" ("<<currCus[cus[i]].ts<<"): "<<"tie"<<endl;
else
{
cout<<cus[i]<<" ("<<currCus[cus[i]].ts<<"):";
qsort(currCus[cus[i]].s,currCus[cus[i]].sc,sizeof(int),com);
for(int j=0;j<currCus[cus[i]].sc;j++)
cout<<' '<<s[currCus[cus[i]].s[j]];
cout<<endl;
}
}
}