poj1015--動態規劃(DP)

《算法導論》第15章

    動態規劃與分治算法

    相同:都是通過組合子問題的解而解決整個問題。

    不同:分治算法是將問題劃分成一些獨立的子問題,遞歸地求解各個子問題,然後合併子問題的解而得到原問題的解。

                 動態規劃適用於子問題不是獨立的情況,也就是各個子問題包含公共的子子問題。在這種情況下,若用分治算法則會做許多不必要的工作,即重複地求解公共的子子問                      題。動態規劃算法對每個重複的子子問題只求解一次,將其結果保存在一張表中,從而避免每次遇到各個子問題時重新計算答案。

    動態規劃與貪心算法

    貪心算法與動態規劃有很多相似之處。貪心算法適用的問題也具有最優子結構。

    區別:在貪心算法中是以自頂向下的方式使用最優子結構的。貪心算法會先做選擇,在當時看來是最優的選擇,然後再求解一個結果子問題,而不是先尋找子問題的最優解,然後再做選擇。

 

    在做poj1015時遇到需要採用動態規劃來解。剛看完題目,不知道要用動態規劃,對動態規劃也不是很瞭解,想了一下,實在沒思路,就上網搜了一下,各位大神都是用動態規劃。實在看不懂呀,只好看一下《算法導論》,算是有點明白了,再來看各位大神的代碼,還是覺得有些混亂呀~~先放放啦,動態規劃,動態規劃呀,對於我來說太難了~~

     參考代碼:http://www.cppblog.com/mythit/archive/2009/06/23/88378.html

    
#include<stdio.h>
#include<string.h>
#include<algorithm>

#define MAX_N 201

int plus[MAX_N];//保存每個人的p和d之和,plus[0]未使用
int sub[MAX_N];//保存每個人的d-p,sub[0]未使用
int f[21][801];//f[j][k]表示在選m個人中的第j個人的時候使所有已選中的人的d,p差爲k時,所能獲得的p,d最大和。因爲1=<m<=20,所以第一維取20+1;因爲每個人的d和p在[0,20]內,所以m個人的D(J)-P(J)在[-20*m,20*m]內,最多有801種取值。因爲下標非負,所以需要加上m*20進行轉化
int path[21][801];//在f[j][k]中選擇的第j個人的編號保存在path[j][k]中
int res[21];

int cmp(const void *a,const void *b)
{
	return *(int*)a-*(int*)b;
}


int main()
{
	int i, j, k, a, b;
	int n,m,case_num=0;
	int p,d;
	int width;


	while(1)
	{
		scanf("%d %d", &n, &m);
		if(n==0 && m==0)
			break;

		for(i=1; i<=n; i++)
		{
			scanf("%d %d",&p, &d);
			
			sub[i] = d-p;
			plus[i] = d+p;
		}
		//scanf("\n");

		case_num++;

		memset(f, -1, sizeof(f));
		memset(path, 0, sizeof(path));

		width = m*20;
		f[0][width] = 0;

		for(j=0; j<m; j++)
		{
			for(k=0; k<=2*width; k++)
			{
				if(f[j][k]>=0)//從f[0][width]開始
				{
					for(i=1; i<=n; i++)
					{
						if(f[j+1][k+sub[i]]<f[j][k]+plus[i])//
						{
							a = j;
							b = k;
							while(a>0 && path[a][b]!=i)//判斷i有沒有被選過
							{
								b -= sub[path[a][b]];
								a--;
							}
							if(a==0)//i沒有被選過
							{
								f[j+1][k+sub[i]] = f[j][k]+plus[i];
								path[j+1][k+sub[i]] = i;
							}
						}
					}
				}
			}
		}

		for(j=0; f[m][width+j]<0&&f[m][width-j]<0;j++);//尋找絕對值最小的
		k=f[m][width+j]>f[m][width-j]?width+j:width-j;//尋找絕對值最小的和最大的

		printf("Jury #%d\n",case_num);
		printf("Best jury has value %d for prosecution and value %d for defence:\n", (f[m][k]-k+width)/2,(f[m][k]+k-width)/2);
		for(i=1;i<=m;i++)
        {
            res[i]=path[m-i+1][k];
            k-=sub[res[i]];
        }

          qsort(res+1,m,sizeof(res[0]),cmp);
          for(i=1;i<=m;i++)
              printf(" %d",res[i]);
		  printf("\n\n");


	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章