日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【nyoj-456】 邮票分你一半 (dp,0-1背包的中点问题)

發(fā)布時(shí)間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【nyoj-456】 邮票分你一半 (dp,0-1背包的中点问题) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題干:

郵票分你一半

時(shí)間限制:1000?ms ?|? 內(nèi)存限制:65535?KB

難度:3

描述

? ? ?小珂最近收集了些郵票,他想把其中的一些給他的好朋友小明。每張郵票上都有分值,他們想把這些郵票分成兩份,并且使這兩份郵票的分值和相差最小(就是小珂得到的郵票分值和與小明的差值最小),現(xiàn)在每張郵票的分值已經(jīng)知道了,他們已經(jīng)分好了,你知道最后他們得到的郵票分值和相差多少嗎?

輸入

第一行只有一個(gè)整數(shù)m(m<=1000),表示測(cè)試數(shù)據(jù)組數(shù)。
接下來(lái)有一個(gè)整數(shù)n(n<=1000),表示郵票的張數(shù)。
然后有n個(gè)整數(shù)Vi(Vi<=100),表示第i張郵票的分值。

輸出

輸出差值,每組輸出占一行。

樣例輸入

2 5 2 6 5 8 9 3 2 1 5

樣例輸出

? ? ??? ?0

? ? ? ? ?2

解題報(bào)告:

? ? ? 這題看起來(lái)很簡(jiǎn)單但是其實(shí)還是有坑的!下面提供n多中ac的代碼。坑就在于 ,sum有可能是奇數(shù)!!

AC代碼:(網(wǎng)絡(luò)版,至今不知為什么這么做可以,普通0-1背包)

#include<bits/stdc++.h>using namespace std; int n; int v[1000 + 5]; int dp[100000 + 5]; int main() {int t;int sum = 0;cin>>t;while(t--) {sum = 0;cin>>n;for(int i = 1; i<= n; i++) {scanf("%d",&v[i]);sum += v[i];}int yuan=sum;sum=(sum+1)/2;//這里直接sum=sum/2 也可以ac!!!即 +1與否不影響if(n == 1) {cout << v[1] << endl; continue;}memset(dp,0,sizeof(dp));for(int i = 1; i<=n; i++) for(int j = sum; j>=v[i]; j--) {if(abs(j-dp[j]) < abs(dp[j-v[i]]+v[i]-j)) dp[j]=dp[j];else dp[j]=dp[j-v[i]]+v[i];}cout << abs(yuan-dp[sum]-dp[sum]) << endl;}return 0 ;}

對(duì)于上面這種方法,我只理解到了下面的內(nèi)容:

? ? ? 因?yàn)楸嘲且活?lèi)問(wèn)題,比如一般的0-1背包,你取dp[j] = max(),是因?yàn)槟阆氲玫絻r(jià)值最大的,所以你這個(gè)狀態(tài)代表的是,前i個(gè)物品在j容量下的最大價(jià)值是dp[j]這么大。?

? ? ? 所以對(duì)于這個(gè)題來(lái)說(shuō)你想得到的是最接近一半的分值,所以你dp[j]中存的應(yīng)該是最接近當(dāng)前價(jià)值的, 即: 如果更接近當(dāng)前價(jià)值,那我就更新他。

AC代碼2:(也是普通0-1背包)

#include<bits/stdc++.h>using namespace std; int n; int v[1000 + 5]; int dp[100000 + 5]; int main() {int t;int sum = 0;cin>>t;while(t--) {sum = 0;cin>>n;for(int i = 1; i<=n; i++) {scanf("%d",&v[i]);sum += v[i];}int half = sum>>1 ;//這里用half = sum>>1 + 1就錯(cuò)了!!但是half = (sum+1)>>1是對(duì)的。。。當(dāng)然你最后輸出的時(shí)候需要加個(gè)abs()才能ac!!!memset(dp,0,sizeof(dp));for(int i = 1; i<=n; i++) {for(int j = half; j>=v[i]; j--) {dp[j] = max(dp[j],dp[j-v[i]] + v[i]);}}cout << sum-2*dp[half]<<endl;} return 0 ;}

最開(kāi)始就是這么寫(xiě)的但是我的輸出相當(dāng)于是cout << 2*(half - dp[half] )<<endl;樣例也過(guò)了,我也覺(jué)著我這樣理解是沒(méi)有問(wèn)題的,舉個(gè)例子:總分值為10,我3他7,那我倆的差值不就是 ?2*((10/2)-3)這么大嗎?如果 ?我4他6,,也可以成立,然后就這么交上去了,然后wa。看了別人的代碼才發(fā)現(xiàn),原來(lái)是奇偶數(shù)出現(xiàn)了問(wèn)題,因?yàn)槲以嚨臉永桥紨?shù)(10嘛),所以肯定看樣例肯定看不出來(lái)問(wèn)題,于是我就改成了保留原sum,新開(kāi)一個(gè)half,然后最后輸出的時(shí)候用sum去減,而不是2*half去減。也就是把最后的輸出改成了AC代碼中那樣的形式。

?

AC代碼3:(裝滿型0-1背包)

#include<bits/stdc++.h>using namespace std; int n; int v[1000 + 5]; int dp[100000 + 5]; int main() {int t;int sum = 0;cin>>t;while(t--) {sum = 0;cin>>n;for(int i = 1; i<=n; i++) {scanf("%d",&v[i]);sum += v[i];} // sum=(sum+1)/2;if(n == 1) {cout << v[1] << endl; continue;}memset(dp,-0x3f3f3f3f,sizeof(dp));dp[0] = 0;for(int i = 1; i<=n; i++) {for(int j = sum; j>=v[i]; j--) {dp[j] = max(dp[j],dp[j-v[i]] + v[i]);}}int minn = 0x3f3f3f3f;for(int i = sum; i>=0; i--) {if(dp[i] >=0) {//cout << 2*(sum-dp[i])<<endl;break;minn = min( abs(sum-dp[i]-dp[i]),minn);}}cout << minn<<endl;}return 0 ;}

這段代碼,是我在寫(xiě)完AC代碼2,然后WA了之后,重新改了一下,不再有sum/=2這樣的操作,而是直接掃到sum,然后從后往前一個(gè)個(gè)的掃,維護(hù)一個(gè)minn,最后輸出minn,這樣也AC了但是時(shí)間是500ms左右,是AC代碼1,AC代碼2 的兩倍左右

總結(jié)

以上是生活随笔為你收集整理的【nyoj-456】 邮票分你一半 (dp,0-1背包的中点问题)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。