新赛季的开始!
(POJ 1015)
前言
(dp) 一般是用于求解最后的值,这个 (dp) 不同于以往的 (dp) ,它需要求解之前的状态,在 (dp) 状态转移的过程中,它的结果总是在不断地移动,在求解完之后,搜索之前的状态时,前面的值已经发生改变,所以需要在状态转移的时候就应该对结果进行保存,这也就是这个题的坑之处,写了一下午。
题解
这个题是一个背包问题,也不能说是背包就是的一些状态转移类型,比较容易理解,坑就在于搜索解。
[dp[i][j] = max(dp[i-(l[k]-r[k])][j-1])
]
这个状态转移方程就是根据之前的结果对于添加这个物品之后状态求解最小值,在求解的过程中趁机对物品状态进行保存,然后特别注意一个样例
输入
1 1
0 0
0 0
输出
Jury #1
Best jury has value 0 for prosecution and value 0 for defence:
1
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 2e3+5;
int vis[MAXN][21],dis[MAXN][21];
int l[205],r[205];
int sum[MAXN][21][2];
vector<int>v[MAXN][21];
inline int min(int a,int b){return a<b?a:b;}
// inline int abs(int a){return a<0?-a:a;}
void slove(int x,int y,int dx,int dy){
v[dx][dy].clear();
for(int i=0;i<(int)v[x][y].size();++i)
v[dx][dy].push_back(v[x][y][i]);
sum[dx][dy][0]=sum[x][y][0];
sum[dx][dy][1]=sum[x][y][1];
}
int main(){
int n,m,times=1;
while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
memset(sum,0,sizeof(sum));
vis[400][0]=1;
for(int i=1;i<=n;++i){
scanf("%d%d",&l[i],&r[i]);
int d=l[i]-r[i];
for(int j=m;j>0;--j){
for(int k=0;k<=800;++k){
if(k+d>=0&&d+k<=800&&vis[k][j-1]){
if(dis[k+d][j]<=dis[k][j-1]+r[i]+l[i]){
vis[k+d][j]=i;
dis[k+d][j]=dis[k][j-1]+r[i]+l[i];
slove(k,j-1,k+d,j);
v[k+d][j].push_back(i);
sum[k+d][j][0]+=l[i];
sum[k+d][j][1]+=r[i];
// printf("%d
",k+d);
// for(int b=0;b<(int)v[k+d].size();++b)
// printf("%d ",v[k+d][b]);
// printf("
");
}
}
}
}
}
// printf("%d
",vis[400][1]);
int minn=2000,L=0,R=0;
for(int i=0;i<=1000;++i){
if(vis[i][m]){
if(abs(minn)==abs(i-400)){
if(L+R<=sum[i][m][0]+sum[i][m][1]){
L=sum[i][m][0];
R=sum[i][m][1];
minn=i-400;
}
}
if(abs(minn)>abs(i-400)){
minn=i-400;
L=sum[i][m][0];
R=sum[i][m][1];
}
}
}
printf("Jury #%d
Best jury has value %d for prosecution and value %d for defence:
",times,L,R);
for(int i=0;i<(int)v[minn+400][m].size();++i)
printf(" %d",v[minn+400][m][i]);
printf("
");
++times;
}
return 0;
}