POJ1837 Balance 題解

題目大意:
一個天平,分別給出c個位置和g個鉤碼的質量,求解所有鉤碼全部放上時有多少種使天平平衡的方案,輸出方案數。

樣例輸入:
2 4// 兩個位置可放鉤碼,共四個鉤碼;
-2 3//兩個位置爲平衡點左邊(感性理解一下)第二格和右邊第三格;
3 4 5 8//四個鉤碼的質量;

樣例輸出:
2//有兩種方案;

樣例解釋:
把四個鉤碼按順序標號爲1,2,3,4,則:
方案一:“-2”位置放1,2,3號鉤碼,“3”位置放4號鉤碼。
//2*(3+4+5)=3*8;
方案二:“-2”位置放2,4號鉤碼,“3”位置放1,3號鉤碼。
//2*(4+8)=3*(3+5);

數據範圍:
2<=c<=20,2<=g<=20,天平長度[-15,15],鉤碼質量[1,25]。

解題思路:
首先,要了解力矩平衡:動力臂乘動力等於阻力臂乘阻力。
然後考慮算法,樸素的枚舉會爆炸。
由於每放一個鉤碼在某一位置上的狀態可由某前一狀態得到,所以自然想到動態規劃。
dp[i][j]代表放上前i個鉤碼後天平偏移量爲j的方案數,則可以得到動歸方程dp[i][j+c[k]*g[i]]=dp[i][j+c[k]*g[i]]+dp[i-1][j],i爲鉤碼枚舉,j爲天平所有位置枚舉,k爲給出的天平位置枚舉。
這裏解釋一下爲什麼是對dp[i][j+c[k]*g[i]]的更新而不是對dp[i][j]的更新。j爲所有位置的循環,只有將前一位置設爲dp[i-1][j]才能將前一位置枚舉全,現一位置便可以更新全,所以現一位置就成了dp[i][j+c[k]*g[i]]。
另外,還存在一些小問題,大家可能注意到了天平左邊的位置是用負數表示的,而數組卻是從0開始。所以我們只能做一下轉化,計算出天平的極限偏移量:20*15*25=7500.開一個長度爲15000的數組,將7500作爲平衡點即可,小於7500爲左偏,反之右偏。
最後有一個優化,由於j代表前一位置偏移量,枚舉時如果發現dp[i-1][j]未被訪問過,就直接continue,不必循環接下來的k,理由是如果不存在狀態dp[i-1][j],就不會存在狀態dp[i][j+c[k]*g[i]]。如何判斷是否訪問過?數組賦零,加一個判零條件就行了。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int c,g,i,j,k;
int dp[25][15000],cc[25],gg[25];
int main(){
    memset(dp,0,sizeof(dp));
    cin>>c>>g;
    dp[0][7500]=1;//此時狀態只有一個:什麼都不放;
    for(i=1;i<=c;i++)
        cin>>cc[i];
    for(i=1;i<=g;i++)
        cin>>gg[i];
    for(i=1;i<=g;i++)
        for(j=0;j<=15000;j++)//最大偏移量7500,所以必須從零開始;
            if(dp[i-1][j])
                for(k=1;k<=c;k++)
                    dp[i][j+cc[k]*gg[i]]=dp[i][j+cc[k]*gg[i]]+dp[i-1][j];
    cout<<dp[g][7500];
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章