这两天碰到一道看似很简单,但是实际做起来确实比较难的问题,在这里分析讨论一下。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1052
3 92 83 71 95 87 74 2 20 20 20 20 2 20 19 22 18 0
200 0 0
因为贪心算法很难想,所以从先从dp入手了,首先对田忌还有王的马都按照从快到慢的顺序排列。
定义f[i][j]为,经过了i场比赛后,田忌从最慢的马开始从后往前使用了j匹马的最大收益
一.
f[i-1][j]表示经过了i-1场比赛后,田忌已经使用了j匹慢马的最大收益。当第i场到来时,为了得到f[i][j],田忌只能使用前面的第i-j匹快马,
此时f[i][j]=f[i-1][j]+(田忌使用第i-j匹马与王的第j匹马拼后的收益,使用函数score(i-j,j)表示)
二.
f[i-1][j-1]表示经过了i-1场比赛后,田忌使用了j-1匹慢马的最大收益。当第i场到来时,为了得到f[i][j],田忌使用了第j匹慢马与王的第i匹快马拼,此时f[i][j]=f[i-1][j-1]+(田忌使用第n-j+1匹马与王的第j匹马拼的最大收益score(n-j+1,j),其中n表示所有总共的马数)
所以:
f[i][j]=max(f[i-1][j]+score(i-j,j),f[i-1][j-1]+score(n-j+1,j))
具体程序实现如下:
#include<stdio.h>
#include<algorithm>
using namespace std;
int a[1001],b[1001],f[1001][1001];
/*
*f[i][j]定义为打了i场比赛,从tanji尾部取出j匹马取得的最大收益
*/
int score(int i, int j){
if(a[i]>b[j])
return 1;
else if(a[i]<b[j])
return -1;
else return 0;
}
//初始化
void init(int n){
//将f全部设置为0
for(int p=0; p<=n; p++)
for(int q=0; q<=n; q++)
f[p][q]=0;
//设置f[i][0],因为无论tianji还是王都是从最快的马按照递减顺序出场
//所以每次只需比较当前的两匹马的大小,并在前一次比较的基础上设置当前f值
for(int i=1; i<=n; i++){
if(a[i]>b[i])
f[i][0]=f[i-1][0]+1;
else if(a[i]<b[i])
f[i][0]=f[i-1][0]-1;
else
f[i][0]=f[i-1][0];//这里容易漏掉,相等就使用前一次的最优值
}
//设置f[i][i],因为每一次都是tianji都是用最慢的马和王最快的马拼,所以tianji从后往前
//王从前往后比较
for(int j=n,g=1; j>=1; j--,g++){
if(a[j]>b[g])
f[g][g]=f[g-1][g-1]+1;
else if(a[j]<b[g])
f[g][g]=f[g-1][g-1]-1;
else
f[g][g]=f[g-1][g-1];
}
}
int maxnum(int i,int j){
if(i>j)
return i;
else
return j;
}
bool compare(const int& a, const int& b){
return a>b;
}
int main(){
int n,max;
while(1){
scanf("%d",&n);
if(n==0)
break;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
}
for(int x=1; x<=n; x++){
scanf("%d",&b[x]);
}
sort(a+1,a+n+1,compare);
sort(b+1,b+n+1,compare);
init(n);
//动态规划递推关系
for(int j=2; j<=n; j++){
for(int k=1; k<j; k++){
f[j][k]=maxnum((f[j-1][k-1]+score(n-k+1,j)),(f[j-1][k]+score(j-k,j)));
}
}
//得到收益最大并记录
max = f[n][0];
for(int g=1; g<=n; g++){
if(f[n][g]>max)
max=f[n][g];
}
printf("%d\n",max*200);
}
}
这个贪心算法确实比较难想,可以参考poj上面的大牛http://poj.org/showmessage?message_id=164719,摘录如下:
贪心的本质在于:田只在有把握赢的情况下拿出快马和王拼,否则用最慢的马比掉王的快马最大程度削弱王的战斗力
* 贪心策略: 1,如果田忌的最快马快于齐王的最快马,则两者比。 (因为若是田忌的别的马很可能就赢不了了,所以两者比) 2,如果田忌的最快马慢于齐王的最快马,则用田忌的最慢马和齐王的最快马比。 (由于所有的马都赢不了齐王的最快马,所以用损失最小的,拿最慢的和他比) 3,若相等,则比较田忌的最慢马和齐王的最慢马 3.1,若田忌最慢马快于齐王最慢马,两者比。 (田忌的最慢马既然能赢一个就赢呗,而且齐王的最慢马肯定也得有个和他比,所以选最小的比他快得。) 3.2,其他,则拿田忌的最慢马和齐王的最快马比。 (反正所有的马都比田忌的最慢马快了,所以这匹马必输,选贡献最大的,干掉齐王的最快马)
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int maxn=1005; int tj[maxn], qw[maxn]; int main() { int n, i, res, max1, max2, min1, min2, cnt; while(~scanf("%d", &n) && n) { for(i=0; i<n; i++) scanf("%d", &tj[i]); for(i=0; i<n; i++) scanf("%d", &qw[i]); sort(tj, tj+n); sort(qw, qw+n); res=0; max1=max2=n-1; min1=min2=0; cnt=0; while((cnt++)<n) { if(tj[max1]>qw[max2]) { res += 200; max1--; max2--; } else if(tj[max1]<qw[max2]) { res -= 200; min1++; max2--; } else { if(tj[min1]>qw[min2]) { res += 200; min1++; min2++; } else { if(tj[min1]<qw[max2]) res -= 200; min1++; max2--; } } } printf("%d\n", res); } return 0; }