這道題目比較短,而且有圖片很容易懂題意,就是每一張牌,分為上下兩部分,上面有幾個點,代表上部分為幾,下面同樣,然後n張牌平行豎直放置,這樣每一張牌的上面部分組成第一行,下面部分組成第二行,上下兩行的和是有差異的值為gap,每一張牌可以上下反一下,這樣可以是的差異值gap縮小,問你使得gap值最小 需要翻牌的最少次數
首先這道題目跟POJ1837有點類似,但是 邊界設置這道題明顯會麻煩許多,因為之前做過了POJ1837,所以這道題一上來就選擇了那個方法,但是a,b值的差異極端為6000,又有負的,為了代表負數 所以數組就得開到12000,這樣n又為1000,就是10的7次方多的這樣就會超時,所以一直在想辦法優化,去推一維的,然後想到了辦法簡直,可以設定一個邊界,然後每次從左邊掃到右邊的范圍 根據a,b的輸入來進行更新,這樣就不用每次都那麼極端的去掃12000了,於是開始敲起來,
因為有a - b可能產生負數,所以就數組開為最大值的兩倍也就是 6000 * 2,然後邊界設定在6000,dp[i][j]代表前i個此時差異值為j的 最少翻轉次數,狀態轉移就是背包的轉移方式
dp[i][j] = min(dp[i][j],dp[i-1][j - (ai - bi)] + 1);
然後就是因為題目所求的值的只是存在的緣故所以可以優化,這個具體的理由這個博客說的很好:戳這裡
但是我一直RE,後來看到這個博主把邊界設定在20000,為什麼是20000呢,我RE肯定是RE在數組的下邊界,因為有個減法的存在可能會導致下標為負數的,一直沒想明白,結果就從下午拖到了晚上,後來發現錯在這裡 ,就是假設gap值為 x ,那麼 某一張牌的 上面值a = 2,下面值b = 1,翻轉以後 gap值會縮小2,也就是x - 2,這樣開6000就不行了,因為極端情況會產生減去12000的情況,最後經過了 20多把的提交終於AC了
而後我又讓一個巨巨去做了一下,他直接用了我一開始想到的那個辦法,也就是我認為會超時的辦法,結果他過了,而且 他第一次交的數組也不夠大,也過了,這道題數據應該是有問題的,同時也給出那個巨巨的代碼
int nnn; int aa[1000 + 55]; int bb[1000 + 55]; int cc[1000 + 55]; int dp[100000 + 55]; int suma,sumb; int sumcc; int le,ri; void init() { suma = sumb = sumcc = 0; memset(aa,0,sizeof(aa)); memset(bb,0,sizeof(bb)); memset(cc,0,sizeof(cc)); memset(dp,0x7f,sizeof(dp)); } int Abs(int x) { return x < 0 ? -x : x; } bool input() { while(cin>>nnn) { for(int i=1;i<=nnn;i++) { cin>>aa[i]>>bb[i]; suma += aa[i]; sumb += bb[i]; cc[i] = aa[i] - bb[i]; sumcc += cc[i]; } return false; } return true; } void cal() { int ans = dp[0]; le = ri = 12000; dp[12000] = 0; for(int i=1;i<=nnn;i++) { if(cc[i] > 0) for(int j=ri + cc[i] * 2;j>=le + cc[i] * 2;j--) dp[j] = min(dp[j],dp[j - cc[i] * 2] + 1); else if(cc[i] < 0) for(int j=le + cc[i] * 2;j<=ri + cc[i] * 2;j++) dp[j] = min(dp[j],dp[j - cc[i] * 2] + 1); ri = max(ri,ri + cc[i] * 2); le = min(le,le + cc[i] * 2); } for(int i=0;i<=nnn;i++) { if(12000 + sumcc - i < 0)continue;/******/ if(ans > dp[12000 + sumcc - i] || ans > dp[12000 + sumcc + i]) { ans = min(dp[12000 + sumcc - i],dp[12000 + sumcc + i]); cout<
巨巨的代碼:
#include#include #include using namespace std; const int N = 1000+5; const int Inf = 1<<30; int dp[N][N<<4], a[N], b[N]; int n; inline void Update(int &x, int y) { if(x > y) x = y; } void solve() { int mid = 6000; for(int i = 0;i <= n; i++) { for(int j = 0;j <= mid*2; j++) dp[i][j] = Inf; } dp[0][mid] = 0; for(int i = 1;i <= n; i++) { for(int j = 0;j <= mid*2; j++) if(dp[i-1][j] < Inf) { Update(dp[i][j+a[i]-b[i]], dp[i-1][j]); Update(dp[i][j-a[i]+b[i]], dp[i-1][j]+1); } } for(int i = 0;i <= mid; i++) if(dp[n][i+mid]