2010年1月18日星期一.sgu220 sgu221
sgu220:n*n的棋盘上放k个象 (n <= 10)
终究还不全是自己想出来的,看到一个提示,放在黑格和白格上的象互不相关。
然后我就开始想状态压缩dp。。。
注意到
+ + + + + + ++ + + + + + ++ + + + + + ++ + + +如果想对+上边放车的情况进行dp,可以把黑格变成这样:
+++++++++++++++++++++++++然后就可以使用放车的方法进行二维dp了。
可惜老夫想歪了,虽然也过了,但是是状态压缩的,只能对于这题有用,对sgu221就不行了.
状态压缩见代码:
int c[bin(N)],bsp,wsp;
LL black[N],white[N];
LL dpblack[N][N][bin(N)], cb[N];
LL dpwhite[N][N][bin(N)], cw[N];
//http://www.cppblog.com/schindlerlee
void preproc()
{
int i;
full = bin(n) - 1;
bsp = wsp = 1;
for (i = 1; i < n; i += 2) {
black[bsp++] = rev(bin(i) - 1) & full;
black[bsp++] = rev(bin(i) - 1) & full;
}
if (i == n) { black[bsp++] = rev(bin(i) - 1) & full; }
for (i = 2; i < n; i += 2) {
white[wsp++] = rev(bin(i) - 1) & full;
white[wsp++] = rev(bin(i) - 1) & full;
}
if (i == n) { white[wsp++] = rev(bin(i) - 1) & full; }
for (i = 1;i <= full;i++) {
int t = i;
while (t > 0) {
c[i] += t & 1;
t >>= 1;
}
}
}
void proc(LL dp[N][N][bin(N)], int terrain[N], int sp,LL res[N])
{
int i, line, s;
dp[0][0][0] = 1;
for (line = 1; line <= sp; line++) {
for (s = 0; s <= full; s++) { dp[line][c[s]][s] += dp[line-1][c[s]][s]; }
for (i = 1; i <= full; i <<= 1) {
if (i & terrain[line]) continue;
for (s = 0; s <= full; s++) {
if(i & s) continue;
dp[line][c[i|s]][i|s] += dp[line-1][c[s]][s];
}
}
}
for (i = 0;i <= k && i <= sp;i++) {
for (s = 0;s <= full;s++) {
res[i] += dp[sp][i][s];
}
}
}
int main()
{
int i;
scanf("%d%d", &n, &k);
preproc();
proc(dpblack, black, bsp-1, cb);
proc(dpwhite, white, wsp-1, cw);
LL res = 0;
for (i = 0;i <= k;i++) {
if(i < bsp && k-i < wsp) // really important
res += cb[i] * cw[k-i];
}
cout << res << endl;
return 0;
}
其实可以发现
如果f(i,j)表示前i行放j个
那么f[i][j] = f[i-1][j] + f[i-1][j-1] * (n(i) - (j-1))
然后程序就变成了这个样子。。
const int N = 101;
LL fb[N][N],fw[N][N];
LL black[N],white[N];
int wsp,bsp,n,k;
void preproc()
{
int i;
bsp = wsp = 1;
for (i = 1; i < n; i += 2) {
black[bsp++] = i;
black[bsp++] = i;
}
if (i == n) { black[bsp++] = i; }
for (i = 2; i < n; i += 2) {
white[wsp++] = i;
white[wsp++] = i;
}
if (i == n) { white[wsp++] = i; }
bsp--,wsp--;
}
void proc(LL dp[N][N],LL terrain[N],int sp)
{
int i,j;
dp[0][0] = 1;
for (i = 1;i <= sp;i++) {
for (j = 0;j <= terrain[i];j++) {
dp[i][j] = dp[i-1][j] + dp[i-1][j-1] * (terrain[i] - j + 1);
}
}
}
int main()
{
int i;
scanf("%d%d",&n,&k);
preproc();
proc(fb,black,bsp);
proc(fw,white,wsp);
LL res = 0;
for (i = 0;i <= k;i++) {
res += fb[bsp][i] * fw[wsp][k-i];
}
cout << res << endl;
return 0;
}
sgu221:n*n的棋盘上放k个象 (n <= 50)
这题由于数据量变大了,所以需要高精度的模板,本人是上的java
//http://www.cppblog.com/schindlerlee/
public class Solution {
static int black[], white[];
static int wsp, bsp, n, k;
static void preproc() {
int i;
bsp = wsp = 1;
for (i = 1; i < n; i += 2) {
black[bsp++] = i;
black[bsp++] = i;
}
if (i == n) {
black[bsp++] = i;
}
for (i = 2; i < n; i += 2) {
white[wsp++] = i;
white[wsp++] = i;
}
if (i == n) {
white[wsp++] = i;
}
bsp--;
wsp--;
}
public static void main(String[] args) {
Scanner cin = new Scanner(
new BufferedReader(
new InputStreamReader(System.in)));
int i, j;
n = cin.nextInt();
k = cin.nextInt();
if(k >= n * 2) {
System.out.println("0");
return;
}
black = new int[456];
white = new int[456];
BigInteger[][] fb = new BigInteger[456][456];
BigInteger[][] fw = new BigInteger[456][456];
preproc();
for (i = 0; i < 456; i++) {
for (j = 0; j < 456; j++) {
fb[i][j] = BigInteger.ZERO;
fw[i][j] = BigInteger.ZERO;
}
}
fb[0][0] = BigInteger.ONE;
for (i = 1; i <= bsp; i++) {
fb[i][0] = BigInteger.ONE;
for (j = 1; j <= black[i]; j++) {
BigInteger tmp = BigInteger.valueOf(black[i] - j + 1);
fb[i][j] = fb[i - 1][j].add(fb[i - 1][j - 1].multiply(tmp));
}
}
fw[0][0] = BigInteger.ONE;
for (i = 1; i <= wsp; i++) {
fw[i][0] = BigInteger.ONE;
for (j = 1; j <= white[i]; j++) {
BigInteger tmp = BigInteger.valueOf(white[i] - j + 1);
fw[i][j] = fw[i - 1][j].add(fw[i - 1][j - 1].multiply(tmp));
}
}
BigInteger res = BigInteger.ZERO;
for (i = 0; i <= k; i++) {
res = res.add(fb[bsp][i].multiply(fw[wsp][k - i]));
}
System.out.println(res);
}
}