题意:N本书放到书架上。每本书有厚度t(1或2)和宽度w。问怎么放书才能让书和书架底面接触的边 厚度最小(示意图见原题)。
思路:一开始是想贪心做的,假设每本书厚度相同,我们一定首先把宽度小的放在上面。所以 先把所有书放在下面,然后把书按宽度/厚度 比排序,之后优先把比值小的放在上面,结果会wa;后来发现是因为不能简单通过比例确定不同厚度书之间的先后关系。如:
4
1 2
2 11
2 11
2 5
1 2优先放在上面,结果是6 ,但是正解应该是把2 5放上去之后的5。
正解:因为正确答案一定是分别从厚度为1和2的书中取了几本宽度最小的,把他们放到上面,让答案最小。所以我们把每本书按照宽度从小到大排序,宽度为1的序列取前i个最优的,宽度为2的序列取前j个最优的,通过枚举i,j找到最优解。
如果有O(n)出结果的方法请dalao指教。一开始贪心贪错了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define de(x) cout << #x << "=" << x << endl
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define lb(x) (x&-(x))
const int N = 101010;
struct book{
int t;
int w;
} a[105],b[105],asum[105],bsum[105];
int n,tmpt,tmpw,anum=0,bnum=0;
int cmp(struct book a,struct book b)
{
return a.w<b.w;
}
int main(){
cin>>n;
rep(i,0,n) {
scanf("%d%d",&tmpt,&tmpw);
if(tmpt==1)
{
a[anum+1].t=tmpt;
a[(anum++)+1].w=tmpw;
}
else
{
b[bnum+1].t=tmpt;
b[(bnum++)+1].w=tmpw;
}
}
sort(a+1,a+anum+1,cmp);
sort(b+1,b+bnum+1,cmp);
rep(i,1,anum+1) {asum[i].t=a[i].t+asum[i-1].t; asum[i].w=a[i].w+asum[i-1].w;}
rep(i,1,bnum+1) {bsum[i].t=b[i].t+bsum[i-1].t; bsum[i].w=b[i].w+bsum[i-1].w;}
int ans=999999;//允许的最小宽度 是否能把最上面的N个放上去
rep(i,0,anum+1) rep(j,0,bnum+1)
{
if(asum[i].w+bsum[j].w<=asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t)
{//上面的总长度<=下面的宽度和
if(ans>asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t)
ans=asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t;
}
}
printf("%d\n",ans);
return 0;
}
思路2: DP dp[i][j][k] 表示前i个数是否能构成下面长j,上面长k的情况。
转移方程:
dp[i][j][k]=(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]);
dp[i][j][k]=(k-a[i].t>=0&&dp[i-1][j][k-a[i]]);
找到所有状态【这里不用保证状态合法,只要最后合法,过程中上面的大于下面的也可以】,找到i=n时最小合法的j就是答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define de(x) cout << #x << "=" << x << endl
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define lb(x) (x&-(x))
const int N = 101010;
struct book{
int t;
int w;
} a[105];
int dp[105][205][205]; //前i本书 下面长为j 上面长为k的情况是否存在
//状态转移方程:
//rep(i,1,n+1) rep(j,1,2*n) rep(k,1,2*n)
//dp[i][j][k]=(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]);
//dp[i][j][k]=(k-a[i].t>=0&&dp[i-1][j][k-a[i]]);
int main(){
int n;
cin>>n;
rep(i,1,n+1) scanf("%d%d",&a[i].t,&a[i].w);
dp[0][0][0]=1;
rep(i,1,n+1) rep(j,0,2*n+1) rep(k,0,2*n+1)
{
if(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]) dp[i][j][k]=1;
if(k-a[i].w>=0&&dp[i-1][j][k-a[i].w]) dp[i][j][k]=1;
}
int flag=0;
rep(i,0,2*n+1)
{
rep(j,0,2*n+1)
{
if(dp[n][i][j]>0&&i>=j)
{
printf("%d\n",i);
flag=1;
break;
}
} if(flag) break;
}
return 0;
}