拔河比赛
拔河比赛:共t组数据,每组数据有N个人,他们的体重分别是W1-Wn,现要分成两组,两组人数差最多为1,体重差最小,求最小体重差的绝对值
对于这个题,首先分析一下如何体重差最小,其实就是体重越接近体重总和一半越好,所以上来先求体重和,把关键的判断部分弄清楚:
minw = min(abs(sum - w * 2),minw);
这里w就是第一组总重,这样我们就把问题简化成求一组总重了;
对于一组,参考背包问题,从头遍历来搜索,每个人都有在第一组和不在第一组两种情况,套深度搜索的格式:
void dfs(int y,int k,int w){ //k为当前取到人数, y为上一个人编号 ,w为总重 if(k == n / 2){ minw = min(abs(sum - w * 2),minw); return; } if(y > n) return; dfs(y+1,k,w); dfs(y+1,k+1,w+a[y]); //取和不取,这是个问题,分两部分 }
这里之前写时有一个误区,就是用for循环遍历,但是咱也不知道循环到的那位参赛者在不在组里,这样想分开讨论就很麻烦,所以优化如上
然后就是主程序段,按照题意设计即可。
int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); sum = 0; for(int j = 1;j <= n;j++){ scanf("%d",&a[j]); sum += a[j]; } minw = 100000; dfs(1,0,0); printf("%d\n",minw); } return 0; }
另外附易错位置:
minw预设要开大点
dfs(1,0,0)这里要注意开始的条件!编号从1开始,同时函数内要取等,还没取到人所以k为0.
然后就是每组数据读入前总重初始化为0
最后是总程序段:
#includeusing namespace std; int a[200],t,n; int sum = 0,minw; void dfs(int y,int k,int w){ //k为当前取到人数, y为上一个人编号 ,w为总重 if(k == n / 2){ minw = min(abs(sum - w * 2),minw); return; } if(y > n) return; dfs(y+1,k,w); dfs(y+1,k+1,w+a[y]); //取和不取,这是个问题,分两部分 } int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); sum = 0; for(int j = 1;j <= n;j++){ scanf("%d",&a[j]); sum += a[j]; } minw = 100000; dfs(1,0,0); printf("%d\n",minw); } return 0; }
总结:遇到新的题型不要慌,其实一些过程复杂的题都可以递归来做,这题抓住了题中人数最多差1的条件,有了n/2这个固定的边界条件,抓住这个突破口即可完成。