栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【无标题】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【无标题】

审判问题 动态规划算法 题目

中文:
描述
在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。选m人的办法是:

控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。为了公平起见,法官选出陪审团的原则是:选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
输入
输入包含多组数据。每组数据的第一行是两个整数n和m,n是候选人数目,m是陪审团人数。注意,1<=n<=200, 1<=m<=20 而且 m<=n。接下来的n行,每行表示一个候选人的信息,它包含2个整数,先后是控方和辩方对该候选人的打分。候选人按出现的先后从1开始编号。两组有效数据之间以空行分隔。最后一组数据n=m=0
输出
对每组数据,先输出一行,表示答案所属的组号,如 ‘Jury #1’, ‘Jury #2’, 等。接下来的一行要象例子那样输出陪审团的控方总分和辩方总分。再下来一行要以升序输出陪审团里每个成员的编号,两个成员编号之间用空格分隔。每组输出数据须以一个空行结束。
样例输入
4 2
1 2
2 3
4 1
6 2
0 0
样例输出
Jury #1
Best jury has value 6 for prosecution and value 4 for defence:
2 3

思路解答

这道题有很多变量,m,审判的总差值,审判总分
我们把n当做常量不去考虑他
但是把剩下的三个全部写到一个二维数组里面f[j][k]
j是m的变量也就是选取的人数
k是差值
f是该差值和人数下最大的总分
然后我们使用f这个函数得到一个递推关系
那就是f[j+1][k+p[i]-v[i]]=f[j][k]+p[i]+v[i] when f[j][k]+p[i]+v[i]>f[j+1][k+p[i]-v[i]] or f[j+1][k+p[i]-v[i]] is empty
需要注意的是这个递推公式是固定f[j][k],得到f[j+1][k+p[i]-v[i]],而不是说f[j+1][k+p[i]-v[i]]有个公式可以由之前的数组得到

其他的全部写在注释里面去了

代码



import java.util.*;
import static java.lang.Math.*;
public class test2 {
    public static void main(String[] args) {
//首先是标准化的输入输出
        Scanner cin =new Scanner(System.in);
        while(cin.hasNext()){
            int n;
            int m;
            n=cin.nextInt();
            m=cin.nextInt();
            int p[]=new int[n+1];
            int v[]=new int[n+1];
            for(int i=1;i<=n;i++){
                p[i]=cin.nextInt();
                v[i]=cin.nextInt();

            }
//            System.out.println();
            
            //这里我们需要一个bias,偏置,因为我们的审判团总差值是一个可正可负的数,但是数组的索引不能是负数,所以加一个偏置导致所有的审判总差值是一个正数
            int bias=m*20;
            //f[j][k]函数表示的是如果选取j个人,且这j个人的差值是k的情况下,最大的审判总分,
            // 最大体现在后面的代码之中有一个判断f[j][k]+p[i]+v[i]>f[j+1][k+p[i]-v[i]],
            // 表示如果有一个更大的值也满足j+1人,差值为k+p[i]-v[i],那么就更新f[j+1][k+p[i]-v[i]]。
            int f[][]=new int [m+1][2*bias+1];
            //注意需要初始化f为-1,这里的-1表示的是还没有一个这样的可能性存在,注意这里的-1有两个作用
            //一个是在初次访问f[j][k]+p[i]+v[i]>f[j+1][k+p[i]-v[i]],的时候f[j+1][k+p[i]-v[i]]总是=1的,所以如果存在这种情况的话总能够保存下来,
            //如果是第二次访问的话,那么就看谁大,放谁,这个就是题目之中说的如果差值相同,选择总分大的那个
            for(int i=0;i<=m;i++){
                Arrays.fill(f[i],-1);
            }
//注意需要由一个初始的值,表示一个人也不选的时候差值为0(k=k_real+bias,所以差值为0 实际在数组之中的索引是bias)   它的总分也是0
            f[0][bias]=0;
            int path[][]=new int [m+1][2*bias+1];
//path是为了找到任意情况下f[j][k]的路径,也就是选取j个人,差值为k的情况下,总分最高的那个选取组合包含了哪些人
            //path的具体意思是对于f[j][k]这种情况,最后一个选取的节点是谁。
            //然后我们通过最后一个节点,知道f[j][k]这种情况,应该是从f[j-1][k-(p[path[j][k]]-v[path[j][k]])]
            //这个意思是,由于我们知道了最后一个节点是path[j][k]],那么我们通过p[] and v[]数组得到最后一个节点的差值p[path[j][k]]-v[path[j][k]]
            //对于选取j个人,差值为k的情况,去掉最后一个人就是选取j-1个人,差值为k-最后一个节点的差值的情况
            //这里隐含了一个认知,就是选取j-1个人,差值为k-最后一个节点的差值的情况下,有很多种不同的选取方法,但是f函数得到的书总分最高的哪一种情况
            //f[j][k]总是选取j-1个人,差值为k-最后一个节点的差值且总分最高的情况下加上最后一个节点的。否则的话f[j][k]就不是总分最高的了;
            //综上所述,最后我们就得到了一个路径,包含了到f[j][k]情况下的所有节点
            for(int i=0;i<=m;i++){
                Arrays.fill(path[i],0);
            }
            int answer[]=new int[m];
//            用来在最后保存选取的人的编号
            for(int j=0;j=0){
//                        一开始的时候其实就是在f[0][bias]开始的
//                        一般情况下就是表示有的差值是靠j个人无法得到的,所以我们从可能的情况下开始做
                        for(int i=1;i<=n;i++){
                            if(f[j][k]+p[i]+v[i]>f[j+1][k+p[i]-v[i]]){
//                                正如上面我们解释为什么f赋初值为-1时所说
//                                 我们判断需要写入的值是否被写入,如果被写入我们写入的是不是比原来大
                                int t1=j;
                                int t2=k;
//                                还要判断这个加入的节点是不是已经在路径之中了
                                while(path[t1][t2]!=i&&t1>0){
                                    t2-=p[path[t1][t2]]-v[path[t1][t2]];
                                    t1--;
                                }
                                if(t1==0){
//                                    r如果不在,就赋值
                                    path[j+1][k+p[i]-v[i]]=i;
                                    f[j+1][k+p[i]-v[i]]=f[j][k]+p[i]+v[i];
                                }
                            }
                        }
                    }
                }
            }
            int res=-1;
            int res_index=-1;
//            接着我们寻找差值最小,总分最大的序列
            for(int i=0;i<=bias;i++){
//                注意我们找差值最小的时候需要从两边开始找,bias右边和bias左边,因为差值有正有负,我们需要的是差值的绝对值最小
                if(f[m][bias-i]!=-1||f[m][bias+i]!=-1){
//                    这是找到了距离bias最近的不是-1的数,也就是差值最小的可能情况,也许有两种情况,所以接下里我们还需要比较那个总分大
                    res_index=f[m][bias-i]>f[m][bias+i]?bias-i:bias+i;
                    res=f[m][res_index];
                    int temp=res_index;
//                    总分大的是res,接下来我们保存这种情况下的节点数
                    for(int j=m;j>=1;j--){
                        answer[j-1]=path[j][temp];
                        temp=temp-(p[ answer[j-1]]-v[ answer[j-1]]);
                    }
                    break;
                }
            }
//按照序号的升序排列
            Arrays.sort(answer);
            System.out.println(Arrays.toString(answer));
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/755686.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号