Jury Compromis-Poj1015-Java版-思維dp

引用晶之王的文字描述:
題意:

從n個人中選出m個人,選法爲控方滿意度之和s1與辯方滿意度之和s2的差的絕對值最小,若有多種方案,則選擇控方滿意度之和s1與辯方滿意度之和s2的和最大的一組,先輸出這是第幾組數據,下一行分別輸出選擇出來的方案中的控方滿意度之和s1與辯方滿意度之和s2。最後一行輸出選擇的人的編號,按從小到大的順序排列。

思路:

用一個二維dp[i][k],第一維i代表已經選擇了的人數,即1——m,第二維k代表枚舉出來的可能出現的控方滿意度之和s1與辯方滿意度之和s2的差,不過這個差可能爲負,即數組的第二維可能爲負,因此我們可以讓它向左偏移一下,具體實現看代碼,這個二維dp的值就代表控方滿意度之和s1與辯方滿意度之和s2的和;則dp[i][k]可由dp[i-1][x]得來的條件是存在某個還沒被選擇的候選人j使得k=x+v[j]。v[j]數組代表第j個人的控方與辯方滿意度之差,s[j]數組代表第j個人的控方與辯方滿意度之和。這個時候我們就可以枚舉1——n中的滿足這個條件的j來動態規劃。輸出要求輸出選擇的人的編號,因此我們用一個path數組來存儲選擇的人的編號,path[i][k]的值爲當前所選人的編號j,i代表這是選擇的第幾個人,與上文的i意義相同,k也與上文的意義相同。這時倒數第二個人的編號就是path[i-1][k-v[path[i][k]]],依次推回去就可得到選擇的所有人的編號。
原文鏈接:https://blog.csdn.net/xinjunwang/article/details/79734103

看了這麼多是不是還不理解,沒關係,我們把樣例走一遍:

4 2 
1 2     no1
2 3     no2
4 1     no3
6 2     no4

我們知道: 第一個人的差值是-1(v[1]=-1),和值是3 (s[1]=3) ,第二個人的差值是-1(v[2]=-1),和值是5 (s[2]=5),以此類推
題目要讓我們找出來兩個人的最優解(這兩個人的差絕對值小於其他人,並且和儘量大)
我們一個一個來:
(1)首先找唯一一個人的最優解:第一次循環

	no1:  差值-1 , 和是 3
	no2: 差值  -1 ,和是5
	no3: 差值3 ,和是5
	no4:差值4  和是8

好了,爲了我們使用方便, (畢竟我們一會要用這個差值作爲數組的下標), 我們讓所有的差值都加上 400

	no1:  差值399, 和是 3
	no2: 差值  399 ,和是5
	no3: 差值403,和是5
	no4:差值404 和是8

我們開一個數組,dp[a][b] , 意思是, 對於已經選出來的 a 個人來說,如果差值(a個人的疊加)是b , dp[a][b]就代表這a個人的 總和值
例如: 我們在第一次循環之後,得到如下結果

dp[1][399] = 5   , 請注意,  如果b一樣,就選擇和最大的那個!
dp[1][403] = 5
dp[1][404]=8     
其他的 dp[1][0] , dp[1][1] ,dp[1][2] .....dp[1][799], dp[1][800]都是初始化的-1,
意思是根本不可能合成 0 , 1, 2 ,799, 800

(2)對於第二次循環:

首先知道,dp[2][1,2,3,…800]都是-1
然後:
我們開始 從 1 找到 800 ,看看哪個數是 第一步走過的數: 僅僅有三個:399 ,403 ,404

對於 399 ,  這是我們在第一步選擇了 no2 所產生的結果
因此 第二步不能選 no2 , 其他的都可以選的 ,那麼我們得到了
dp[2] [ 399  +  -1  ] =  3+3=6  //選擇第一個人
dp[2][399  +    3]  = 3+5=8;   //選擇第三個人
dp[2][399 +   4] = 3+8 = 10   //選擇第四個人

對於 403,這是第一部選擇了第三個人的後果,因此第二步只能選 1 ,2, 4了
dp[2][403 +  -1] = 5+  3 =8 // 選第一個人
dp[2][403 +  -1]=5+5 =10  // 選第二個人
dp[2] [403 +  4] = 5+8 =13   //選第三個人

對於404也是一樣

我們又發現,出現了兩個 dp[2][403-1] , 還是老道理,選擇 和比較大的 ,那麼就是10

(3) 走完了上面兩步,其實dp就結束了, 我們此使得到的 dp數組 我們想要:
dp[2][x] 中的x 最貼近 400 , 想想爲什麼,因爲 400 就是0 (我們上面擔心負數因此加了400), 所以,遍歷一次 dp[2][] ,找出來那個 離 400 最近的 x ,比如 dp[2][399] 就比 dp[2][402] 更近。
實際的值就是 -1比2 更近。

這就是這個題目的思想, 程序中的判斷是否 使用過 和 記錄路徑相信大家一看就知道了

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;


public class poj1015 {
	static int[][] dp, path;
	static int[] 	v,s;
	public static void main(String[] args) {
		Scanner sc = new Scanner(new BufferedInputStream(System.in));
		dp=new int[25][2000]; 
		path=new int[25][2000]; 
		v=new int[300];
		s=new int[300];
		List<Integer>list = new ArrayList<Integer>();
		int tt=0;
		while(sc.hasNext()) {
			tt++;
			int n =sc.nextInt();
			int m=sc.nextInt();
			if(n==0 &&m==0)  break;
			for(int i=0;i<n;i++) {int t1 =sc.nextInt(); int t2= sc.nextInt(); v[i]=t1-t2;s[i]=t1+t2;}
			for(int i=0;i<25;i++)for(int j=0;j<2000;j++) {dp[i][j]=-1;path[i][j]=-1;}
			
			dp[0][400]=0;
			for(int i=0;i<m;i++) {
				for(int k=0;k<=800;k++) {
					if(dp[i][k] < 0) continue; 
					for(int j=0;j<n;j++) { 
						if(dp[i+1][k+v[j]] < dp[i][ k]+s[j]) {   
							// 這個j 是否用過,比如 第一次循環對應的這個 k用過1號,
							//這裏就能不能再是1號 , 可以是其他人,
							int a = k;
							int b =i;
							while(b>0 && path[b][a]!=j) {
								a-=v[path[b][a]];
								b--;
							}
							if(b==0) {
								dp[i+1][k+v[j]] = dp[i][k]+s[j];
								path[i+1][k+v[j]]=j;
							}
						}
					}
				}
			}
			int a=400;
			int b= 0;
			 //找最貼近400的值
			while(dp[m][a-b] <0 && dp[m][a+b]<0) {  
				b++;
			}
			int res=0;
			//和大的那個
			if(dp[m][a-b]>dp[m][a+b]) 
				res=a-b;
			else 
				res=a+b;
			System.out.println("Jury #"+tt);
			System.out.println("Best jury has value "+(res-400+dp[m][res])/2+" for prosecution and value "+(400-res+dp[m][res])/2+" for defence: ");
			int temp;
			list.clear();
			for(int i=0;i<m;i++) {
				temp=path[m-i][res];
				list.add(temp);
				res-=v[temp];
			}
			Collections.sort(list);
			for(int i:list) {
				System.out.print(" "+(i+1));
			}
			System.out.println();
		}
	}
}

反思: 這題寫了好久,也參考了別人。這種題爲啥那麼難想,是不是自己做的少?理解題意是一部分, 思維和經驗是一部分,太弱了 ,欲哭無淚

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