状压汇总

SGU 222:

很明显答案是\(C_n^k*A_n^k\)

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int n,k;
LL ans[N];
void pre(){
    ans[1] = 1,ans[0] = 1;
    for(int i = 2;i < 20;i++) ans[i] = ans[i - 1] * i;
}
void init(){
	n = read(),k = read();
    if(n < k) printf("0\n");
    else printf("%lld\n",ans[k] * (ans[n] / ans[k] / ans[n - k]) * (ans[n] / ans[k] / ans[n - k]));
}
int main(){
    pre();
    init();
}

SGU223:

这个题是统计方案,依然是逐行转移,如果某个格子\((i,j)\)放置了国王,那么上一行\(j,j-1,j+1\),这三个格子都是他的攻击范围,因此这三个格子是不能够放国王的,由于我们需要的答案是恰好放置\(k\)个国王的方案数,所以方程还需要加一维,记录放置的国王数。先预处理出一行的合法状态(用\(s\)数组记录)以及放置(\(sum\)数组记录)的国王个数方程表示为:\(dp[i][a][k]+=dp[i-1][b][k-sum[a]](s[a]\&s[b]==0)\)

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
const int maxn = 15, maxk = 105, sz = 10;
struct Trans{
    int a,b,ch1;
    Trans(int a = 0,int b = 0,int ch1 = 0):a(a),b(b),ch1(ch1){}
};
int n,k;
Trans ts[1<<(sz<<1)]; int tl;
LL f[maxn][maxk][1<<sz], ans;
void DFS(int u,int lst,int now,int cnt){
    if(u == n) ts[++tl] = Trans(lst,now,cnt); 
    else{
        DFS(u + 1, lst, now, cnt);
        if(!u||(!(lst & (1<<(u - 1))) && !(now & (1<<(u - 1))))){
            DFS(u + 1,lst|(1 << u),now,cnt);
            DFS(u + 1,lst,now|(1 << u),cnt + 1);
        }
    }
}
int main(){
    n = read(),k = read();
    f[0][0][0] = 1;
    DFS(0,0,0,0);
    for(int i = 1; i <= n; i++) for(int j = 0; j <= k; j++) for(int p = 1; p <= tl; p++) if(j - ts[p].ch1 >= 0) f[i][j][ts[p].b] += f[i - 1][j-ts[p].ch1][ts[p].a];
    for(int i = 0; i < (1<<n); i++) ans += f[n][k][i];
	cout << ans;       
    return 0;
}

poj 1321:

看到的第一眼,觉得和n皇后问题很像,再仔细一看,就是很像啊,方法相同,只是本题只要判断不在一行或者一列,规定了棋子放置位置,故要增加判断条件,dfs就可。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
char a[10][10];
int vis[10],n,k,tot,m; 
void DFS(int cur){
    if(k == m){
        tot++;
        return ;
    }
    if(cur >= n) return ;
    for(int j = 0; j < n; j++)
        if(vis[j] == 0 && a[cur][j] == '#'){
            vis[j] = 1,m++;                 
            DFS(cur + 1),vis[j] = 0;
            m--;
        }
    DFS(cur + 1);
}
int main(){
    while((n = read(),k = read()) && n != -1 && k != -1){
        tot = 0,m = 0;
        for(int i = 0; i < n; i++) scanf("%s",&a[i]);
        memset(vis,0,sizeof(vis));
        DFS(0);
        printf("%d\n",tot);
    }
    return 0;
}

poj 1185:

由于当前行和前两行有关系,所以得用3维矩阵来保存一个状态下最多的炮兵个数,用\(dp[i][curst][prest]\)表示当前第\(i\)行状态对\(curst\),前一行状态为\(prest\)的最大炮兵数。

转移方程为\(dp[i][curst][prest]=max \left\{dp[i-1][prest][preprest]\right\}\),这样求到最后一行之后,答案就是最后一行所有状态中最大的那个。程序初始化的时候需要对第一行进行预处理,设置\(dp[0][st][0]=st\)合法\(\&st\)\(1\)的个数。这样进行下面的计算的时候,由于0状态肯定是和所有状态兼容的,所以就不会影响计算结果。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
#define me(a) memset(a,0,sizeof(a))
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int f[115][70][70],a[105],g[70],num[70],m,n,len;
char str[111];
int main(){
    while(~scanf("%d%d",&n,&m)){
        me(f),me(num);
        me(a),me(g);
        for(int i = 1; i <= n; i++){
            scanf("%s", str);
            for(int j = 0; j < m; j++) if(str[j] == 'H') a[i] += (1<<j);
        }
        len = 1;
        for(int i = 0;i < (1<<m); i++){
            if(!(i&(i<<1)) && !(i&(i<<2))){
                int k = i, sum = 0;
                while(k){
                    if(k&1) sum++;
                    k >>= 1;
                }
                num[len] = sum;
                g[len++] = i;
            }
        }
        for(int i = 1; i < len; i++) if(!(a[1]&g[i])) f[1][i][1] = num[i];
        for(int i = 2; i <= n; i++){
            for(int j = 1; j < len; j++){
                if(g[j]&a[i]) continue;
                for(int k = 1; k < len; k++){
                    if(g[j] & g[k]) continue;
                    if(g[k] & a[i-1]) continue;
                    for(int t = 1; t < len; t++){
                        if(g[t] & g[j]) continue;
                        if(g[t] & g[k]) continue;
                        if(g[t] & a[i - 2]) continue;
                        f[i][j][k] = max(f[i][j][k], f[i-1][k][t] + num[j]);
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 1;i < len; i++) for(int j = 1;j < len; j++) ans = max(ans, f[n][i][j]);
        printf("%d\n", ans);
    }
    return 0;
} 

hdu 2640:

状态是每个物体的第三行...
由于第二行的特殊性,加上不能重叠,可以知道第三行状态很少一共有:
00000000 , 01001000
01000000 , 01000100
00100000 , 01000010
00010000 , 00100100
00001000 , 00100010
00000100 , 00010010
00000010
13种.

所以我们求出所有合法状态就可了。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int t,n,m,f[2][13][13],num,vis[13],cnt[13];
char map[105][10];
void init(){
    vis[0] = cnt[0] = 0;
    num = 1;
    for(int i = 1;i + 1 < m;i++){
        vis[num] = (1 << i),cnt[num] = 1,num++;
        for(int j = i + 3;j + 1 < m;j++) vis[num] = (1 << i) + (1 << j),cnt[num] = 2,num++;
    }
}
bool ch1(int st,int i){
    for(int j = 0;j < m;j++) if(st & (1<<j)) if(map[i][j]=='#' || map[i-1][j]=='#' || map[i-1][j-1]=='#' || map[i-1][j+1]=='#' || map[i-2][j]=='#') return 0;
    return 1;
}
 bool ch2(int x,int y){
    int a[3][8];
    memset(a,0,sizeof(a));
    for(int i = 0;i < 8;i++){
        if(x&(1<<i)) a[0][i]++,a[1][i]++,a[1][i-1]++,a[1][i+1]++,a[2][i]++;
        if(y&(1<<i)) a[1][i]++,a[2][i]++,a[2][i-1]++,a[2][i+1]++;
    }
    for(int i = 0;i < 8;i++) if(a[0][i] > 1 || a[1][i] > 1 || a[2][i] > 1) return 0;
    return 1;
}
 bool ck3(int x,int y){
    for(int i = 0;i < m;i++) if(x & (1<<i)) if(y & (1<<i)) return 0;
    return 1;
}
void solve(){
	init();
    int now = 0;
    memset(f[now],0,sizeof(f[now]));
    for(int i = 2;i < n;i++){
        now ^= 1;
        memset(f[now],0,sizeof(f[now]));
        for(int j = 0;j < num;j++){
            int x = vis[j];
            for(int k = 0;k < num;k++){
                int y = vis[k];
                for(int r = 0;r < num;r++){
                    int z = vis[r];
                    if(!ch1(z,i)) continue;
                    if(!ch2(z,x)) continue;
                    if(!ck3(z,y)) continue;
                    f[now][r][j] = max(f[now][r][j],f[1-now][j][k]+cnt[r]);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0;i < num;i++) for(int j = 0;j < num;j++) ans = max(ans,f[now][i][j]);
    printf("%d\n",ans);
}
int main(){
	t = read();
    while(t--){
        n = read(),m = read();
        for(int i = 0;i < n;i++) scanf("%s",map[i]);
        if(n <= 2||m <= 2 ) puts("0");
        else solve();
    }
    return 0;
}

poj 2411:

1.\(h*w\)若为奇数,则无解。

2.按行处理。处理第\(i\)行时,保证前\(i-1\)行全部覆盖,当前行的状态为\(state\)

\(f[i][state]\)表示处理到第\(i\)行,第\(i\)行的状态为\(state\),且前\(i-1\)行全部填满的方案数。

\(f[i][state]=∑f[i-1][state2]\)

需要统计有哪些\(state2\)可以转移到\(state\),可以用\(dfs\)来预处理出来。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e3 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
const int maxm = 12;
LL f[15][1 << maxm],num;
int n,m,maxx,ans;
void updata(int i , int opt, int pos ){
    if (pos == m){
        f[i][opt] += num;
        return;
    }
    updata(i,opt,pos + 1);
    if(pos < m && !(1 << pos - 1 & opt) && !(1 << pos & opt)) updata( i , opt | (1 << pos - 1) | (1 << pos) , pos + 1);
}
void solve(){
    memset(f , 0 , sizeof(f));
    num = 1;
    maxx = (1 << m) - 1;
    updata(1 , 0 , 1);
    for(int i = 2; i <= n; ++i)
        for(int opt = 0; opt < 1 << m; ++opt){
            if (f[i - 1][opt]) num = f[i - 1][opt];
            else continue;
            updata(i , ~ opt & maxx , 1 );
        }
    printf("%lld\n",f[n][maxx]);
}
int main(){
	n = read(),m = read();
    while (n && m){
        if (n < m) swap(n,m);
        solve();
        n = read(),m = read();
    }
}

hdu 2280:

我们可以用\(dp[i][num][state]\)表示能否达到在第\(1\)~\(i-1\)行填满的情况下,用了\(num\)\(1*1\),第i行状态为\(state\)的状态。我们遍历每一行,每种\(num\),每种状态,通过\(dfs\)来尝试放各种棋子,要判断好每种棋子能不能放

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e3 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int g[N],n,m,f[N][1<<6];
void dfs(int i,int k,int state1,int state2,int v){
    if(k > 5){
        f[i][state2] = max(f[i][state2] , v);
        return;
    }
    if(k <= 4){
        if(!(state1&(1 << k - 1)) && !(state2 & (3 << k - 1))) dfs(i,k + 1,state1 | (1 << k - 1),state2 | (3 << k - 1),v + 3);
        if(!(state1&(1 << k)) && !(state2 & (3 << k - 1))) dfs(i,k + 1,state1 | (1 << k),state2 | (3 << k - 1),v + 3);
        if(!(state1&(3 << k - 1)) && !(state2 & (1 << k - 1))) dfs(i,k + 1,state1 | (3 << k - 1),state2 | (1 << k - 1),v + 3);
        if(!(state1&(3 << k - 1)) && !(state2 & (1 << k))) dfs(i,k + 1,state1 | (3 << k - 1),state2 | (1 << k),v + 3);
        if(!(state2&(3 << k - 1))) dfs(i,k + 1,state1,state2 | (3 << k - 1),v + 2);
	}
    else if(!(state1&(1 << k - 1)) && !(state2 & (1 << k - 1))) dfs(i,k + 1,state1|(1 << k - 1),state2|(1 << k - 1),v + 2);
    dfs(i,k + 1,state1,state2,v);
}
void dfs2(int k,int sta,int v){
    if(k > 5){
        f[1][sta] = max(f[1][sta] , v);
        return;
    }
    if(k <= 4) if(!(sta & (3<<k - 1))) dfs2(k+1 , sta | (3<<k-1) , v+2);
    dfs2(k + 1,sta,v);
}
int main(){
    char a;
    while(scanf("%d%d",&n,&m) != EOF){
        memset(g,0,sizeof(g));
        int all = 0;
        for(int i = 1; i <= n ; i++)
            for(int j = 1; j <= 5 ; j++){
                cin>>a;
                if(a == '1') g[i] |= (1<<j-1);
                else all++;
            }
        memset(f , -1 , sizeof(f));
        f[1][g[1]] = 0;
        dfs2(1,g[1],0);
        for(int i = 2;i <= n;i++) for(int j = 0; j < (1 << 5) ; j++) if(f[i - 1][j] >= 0) dfs(i,1,j,g[i],f[i - 1][j]);
        int maxn = 0;
        for(int i = 0;i < (1 << 5);i++) maxn = max(maxn , f[n][i]);
        int remain = all - maxn;
        if(remain <= m) puts("YES");
        else puts("NO");
    }
    return 0;
}

poj 1038:

乱搞就行了

\(Dfs\)优化状压\(dp\) \(O(我不知道,反正过不了,需要再优化)\)(理论上80%)

再剩下的,卡常数+卡常数+一个小优化(有可能被卡一个点)

以上属于乱搞,正解在下面

\(O(3^{10}*N)\),我们知道,设,我们更新第\(i\)行的状态,那么如果第\(i-1\)行的第\(j\)个位置不能被选取,则第\(i-2\)行的第\(j\)个位置同样不可以被选取,那么\(4^N\)状态转化为\(3^N\),理论时间复杂度可能会超时(这个大概是60-70%的样子),所以需要用到\(Dfs\)优化(这个大概70-100%,有的人就能,反正我没有),之后同样,用链式前向星优化一下,就可以过了,当然,还可以继续优化。

下面是乱搞的代码

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 155;
const int M = 205;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int f[3][M][M],cur[N],n,m,K,can[M],p[1 << 15],cnt2,head[M][M];
int to[M * M * 30],to2[M * M * 30],to3[M * M * 30],cnt[M * M * 30],nxt[M * M * 30];
int a,b,ans,cnt4;
void dfs(int f1,int f2,int step,int f3,int cnt1){
    if(to[cnt4] != f1 || to3[cnt4] != f2){
        to[++cnt4] = f1;
        to2[cnt4] = f3;
        to3[cnt4] =f2;
        cnt[cnt4] = cnt1;
        nxt[cnt4] = head[a][b];
        head[a][b] = cnt4;
    }
    if(step >= m)return ;
    if(step < m - 1 && !(f1 & (3 << step)) && !(f2 & (3 << step))) dfs(f1|(3<<step),f2|(3<<step),step+2,f3|(3<<step),cnt1+1);
    if(step < m - 2 && !(f1 & (7<<(step)))) dfs(f1 | (7 << step),f2,step + 3,f3 | (7 << step),cnt1 + 1);
    if(step < m -  2&& !(f1 & (7<<step)) && !(f2 & (7 << step))) dfs(f1|(7<<step),f2|(7 << step),step + 3,f3,cnt1 + 1);
    dfs(f1,f2,step + 1,f3,cnt1);
    return ;
}
void init(){
    memset(cur,0,sizeof(cur));
    memset(head,0,sizeof(head));
    cnt4 = 0;
}
int main(){
    int T = read();
    while(T--){
        init();
        n = read(),m = read(),K = read();
        int mask = (1 << m) - 1;
        for(int i = 1;i <= K;i++){
            int x = read(),y = read();
            cur[x] |= (1 << (y - 1));
        }
        cur[0] = cur[n + 1] = mask;
        ans = 0,cnt2 = 0;
        for(int j = 0;j <= mask;j++){
            int s = j;
            if(((s & 3) == 1) || (s != 0 && ((((~s)) & ((~s) << 2)) && (((((~s)) & ((((~s))) << 2)) >> 1) & s))))continue;
            can[++cnt2] = j;
            p[j] = cnt2;
        }
        for(int i = 1;i <= cnt2;i++){
            for(int j = 1;j <= cnt2;j++){
                if((can[i] & can[j]) != can[i]) continue;
                a = i,b = j;
                dfs(can[i],can[j],0,0,0);
            }
        }
        memset(f[1],0,sizeof(f[1]));
        for(int i = 1;i < n;i++){
            memset(f[(i + 1) & 1],0,sizeof(f[(i+1)&1]));
            for(int j = 1;j <= cnt2;j++){
                if(can[j] & cur[i])continue;
                for(int k = j;k <= cnt2;k++){
                    if((can[k] & cur[i - 1]))continue;
                    if((can[k] & can[j]) != can[j])continue;
                    for(int l = head[j][k];l;l = nxt[l]){
                        if((to[l] & cur[i])||(to3[l] & cur[i - 1]) || (to2[l] & cur[i + 1]))continue;
                        f[(i ^ 1) & 1][p[to2[l]]][p[to[l]|to2[l]]] = max(f[(i ^ 1) & 1][p[to2[l]]][p[to[l]|to2[l]]],f[i & 1][j][k]+cnt[l]);
                    }
                    if(i==1)break;
                }
            }
            for(int j = 1;j <= cnt2;j++) for(int k = 1;k <= cnt2;k++) ans = max(ans,f[(i + 1) & 1][j][k]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

hdu 2442:

状态就是 占有这块 和 不占有这块
$dp[ i ][ j ][ k ] $代表 前 \(i\) 行 第$ i \(行状态为\) j \(第\) i-1 \(行状态为\) k $的最大面积
可以首先用 \(dfs\) 初始化出 \(i=2\) 的状态
再来统计答案即可。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
typedef long long LL;
const int maxn = 1e2 + 5;
const int sz = 1<<6;
const int mod = 1e9 + 7;
const int inf = 2e9 + 7;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int n,m,dp[maxn][sz][sz];
void dfs(int s,int u,int v,int num,int pos,int t){
    if(pos>=m){
        if(dp[t][s][u]<num) dp[t][s][u] = num;
        return ;
    }
    if(pos > 0){
        if(!(s & (1<<pos)) && !(u & (3<<pos-1)) && !(v & (1<<pos))) dfs(s|(1<<pos),u|(3<<pos-1),v|(1<<pos),num+4,pos+1,t);
        if(!(s & (1<<pos)) && !(u & (1<<pos)) && !(v & (3<<pos-1))) dfs(s|(1<<pos),u|(1<<pos),v|(3<<pos-1),num+4,pos+1,t);
    }
    if(pos>1){
        if(!(s & (1<<pos-1)) && !(u & (7<<pos-2)) && !(v&(1<<pos-1))) dfs(s|(1<<pos-1),u|(7<<pos-2),v|(1<<pos-1),num+5,pos+1,t);
        if(!(s & (1<<pos-1)) && !(u & (7<<pos-2))) dfs(s|(1<<pos-1),u|(7<<pos-2),v,num+4,pos+1,t);
        if(!(s & (1<<pos)) && !(u & (7<<pos-2))) dfs(s|(1<<pos),u|(7<<pos-2),v,num+4,pos+1,t);
    }
    dfs(s,u,v,num,pos + 1,t);
}
void dfs2(int s,int u,int num,int pos){
    if(pos >= m){
        if(dp[2][s][u]<num) dp[2][s][u] = num;
        return ;
    }
    if(!(s & (1<<pos-1)) && !(u&(7<<pos-2))) dfs2(s|(1<<pos-1),u|(7<<pos-2),num +4 ,pos + 3);
    if(!(s & (1<<pos)) && !(u&(7<<pos-2))) dfs2(s|(1<<pos),u|(7<<pos-2),num + 4,pos + 3);
    dfs2(s,u,num,pos+1);
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        if(n==1||m==1) {puts("0"); continue;}
        memset(dp,-1,sizeof dp);
        dfs2(0,0,0,2);
        int num = 1 << m;
        for(int i = 3;i <= n;i++)
        for(int j = 0;j < num;j++) for(int k = 0;k < num;k++) if(dp[i-1][j][k] >= 0) dfs(0,j,k,dp[i-1][j][k],1,i);
        int ans = 0;
        for(int i = 0;i < num;i++) for(int j = 0;j < num;j++) if(ans < dp[n][i][j]) ans = dp[n][i][j];
        cout<<ans<<endl;
    }
    return 0;
}

hdu 1755:

这道题直接暴力枚举复杂度为 \(n!*m\)

但是\(k<100\) , 所以我们可以通过取模用\(dp[i][j]\) 表示\(k=i\) 时,\(-x\)取模\(k\)\(j\)的最小值

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int vis[10],n,m,x,k,num[10],dp[102][102];
void dfs(int t , int state){
    if(t >= n){
        for(int i = 1;i <= 100;i++) if(dp[i][state % i] > state) dp[i][state % i] = state;
        return;
    }
    for(int i = 0;i < n;i++){
        if(!vis[i]){
            vis[i] = 1;
            dfs(t + 1,state * 10 + num[i]);
            vis[i] = 0;
        }
    }
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i = 0;i < n;i++) num[i] = read();
        memset(vis,0,sizeof(vis));
        for(int i = 0;i <= 100;i++) for(int j = 0;j <= 100;j++) dp[i][j] = inf;
        for(int i = 0;i < n;i++){
            if(num[i]){
                vis[i] = 1;
                dfs(1,num[i]);
                vis[i] = 0;
            }
        }
        for(int i = 0;i < m;i++){
            x = read(),k = read();
            if(dp[k][(((-x) % k) + k) % k] < inf) printf("%d\n",dp[k][(((-x)%k)+k)%k]); 
            else puts("None");
        }
    }
    return 0;
}

hdu 1820:

象的攻击路线是斜的,所以我们可以将棋盘旋转45°,这样攻击路线就成了水平,象就变成了车

之后可以发现,如果将棋盘分为黑白格子,黑白棋子之间是无法互相攻击的,那我们就可以将他们分开考虑

把棋盘处理成2个下面的图形

img

\(dp[i][j]\)表示前\(i\)行放了\(j\)个车的方法数,\(c[i]\)表示第\(i\)行可以放置的棋子数量,那么转移方程为:

\(dp[i][j] = dp[i-1][j] + dp[i-1][j-1] * (c[i] - (j - 1))\)

需要注意的是\(c\)数组应该是增序的,这样才能保证前面的\(j-1\)行放了车,对应这一行就有\(j-1\)个位

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 70;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int n,k,c1[N],c2[N],dp1[10][N],dp2[10][N];
void init(){
    memset(c1,0,sizeof(c1));
    memset(c2,0,sizeof(c2));
    for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) 
		if((i + j) & 1) c2[(i+j)>>1]++; 
		else c1[(i+j)>>1]++;
}
void solve(int dp[N][N],int c[N]){
    for(int i = 0;i <= n;i++) dp[i][0] = 1;
    for(int i = 1;i <= n;i++) for(int j = 1;j <= c[i];j++) dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] * (c[i] - j + 1);
}
int main(){
    while(scanf("%d%d",&n,&k)&&n+k){
        init();
        sort(c1 + 1,c1 + 1 + n),sort(c2 + 1,c2 + n);
        memset(dp1,0,sizeof(dp1)),memset(dp2,0,sizeof(dp2));
        solve(dp1,c1),solve(dp2,c2);
        int ans = 0;
        for(int i = 0;i <= k;i++) ans += dp1[n][i] * dp2[n - 1][k - i];
        printf("%d\n",ans);
    }
    return 0;
}

hdu1668:

\(dp[state][i][j]\)表示\(state\)状态下倒数第二个岛为\(i\),最后一个岛为\(j\)时的最优解,\(g[state][i][j]\)为相应的路径数目,其中\(state\)的二进制表示的\(i\)位为\(1\)表示岛\(i\)被访问过,反之为\(0\)
则显然当有边\((i,j)\)存在时,有如下初值可赋:
\(dp[(1<<i)+(1<<j)][i][j]=val[i]+val[j]+val[i]*val[j],g[(1<<i)+(1<<j)][i][j]=1\)
如果状态\((state,i,j)\)可达,检查岛\(k\),如果此时\(k\)没有被访问过并且有边\((j,k)\)存在,则做如下操作:
1)设tmp为下一步访问岛k时获得的总利益,\(r=state+(1<<k)\)
2)如果\(t,p>dp[r][j][k]\),表示此时可以更新到更优解,则更新:
\(dp[r][j][k]=q,g[r][j][k]=g[state][i][j]。\)
3)如果\(tmp==dp[r][j][k]\),表示此时可以获得达到局部最优解的更多方式,则更新:
$ g[r][j][k]+=g[p][i][j]。\( 最后检查所有的状态\)((1<<n)-1,i,j)$,叠加可以得到最优解的道路数。
需要注意的是,题目约定一条路径的两种行走方式算作一种,所以最终结果要除\(2\)

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
int n,m,val[15],map[13][13],dp[1<<13][13][13];
LL g[1<<13][13][13];
int main(){
    int t = read();
    while(t--){
    	n = read(),m = read(); 
        for(int i = 0;i < n;i++) val[i] = read();
        memset(map,0,sizeof(map)); 
        while(m--){
        	int u = read()-1,v = read()-1; 
            map[u][v] = map[v][u] = 1;
        }
        if(n==1){
            printf("%d 1\n",val[0]);
            continue;
        }
        memset(dp,-1,sizeof(dp));
        memset(g,0,sizeof(g));
        for(int i = 0;i < n;i++) for(int j = 0;j < n;j++) if(i != j && map[i][j]) dp[(1<<i)|(1<<j)][i][j] = val[i] + val[j] + val[i] * val[j],g[(1<<i)|(1<<j)][i][j] = 1;
        for(int i = 0;i < (1<<n);i++)
            for(int j = 0;j < n;j++)
                if((i & (1<<j)) != 0)
                    for(int k = 0;k < n;k++)
                        if(map[j][k] && j!=k && (i&(1<<k))!=0 && dp[i][j][k]!=-1)  
                            for(int x=0;x<n;x++)
                                if(map[k][x] && j!=x && k!=x && (i&(1<<x))==0){
                                    int tmp = dp[i][j][k]+val[x]+val[k]*val[x];
                                    if(map[j][x]) tmp+=val[j]*val[k]*val[x];
                                    if(dp[i|(1<<x)][k][x]<tmp) dp[i|(1<<x)][k][x] = tmp,g[i|(1<<x)][k][x] = g[i][j][k];
                                    else if(dp[i|(1<<x)][k][x] == tmp) g[i|(1<<x)][k][x] += g[i][j][k];
                                }
        int ans1 =0;
        LL ans2 = 0;
        for(int i = 0;i < n;i++) for(int j =0 ;j < n;j++)
            if(i!=j && map[i][j]){
                if(ans1 < dp[(1<<n) - 1][i][j]) ans1 = dp[(1<<n) - 1][i][j],ans2 = g[(1<<n) - 1][i][j];
				else if(ans1 == dp[(1<<n) - 1][i][j]) ans2+=g[(1<<n) - 1][i][j];
            }
        cout<<ans1<<" "<<ans2 / 2<<endl;
    }
    return 0;
}

hdu 2518:

思路比较简单,不过代码量比较大。。

需要判断出每个图形有几种旋转方式。 建立矩阵,列数数72,60个小方格,外加12个图形限制,每个图形只能用一次。。

因为只有6中情况,所以要先用自己的代码跑出每组例子,然后直接输出结果就ok了。

若是直接把代码提交上去而不进行处理,肯定TLE,于是先用程序把所有答案跑出啦再打表输出。

Code
#define B cout << "BreakPoint" << endl;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << " ";
#define Msz(x) cout << "Sizeof " << #x << " " << sizeof(x)/1024/1024 << " MB" << endl;
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#define LL long long
const int inf = 1e9 + 9;
const int N = 2e5 + 5;
using namespace std;
inline int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-')
			w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * w;
}
using namespace std;
int ans[]={0,0,0,2,368,1010,2339};
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m) != -1){
        if(n>m) swap(n,m);
        printf("%d\n",ans[n]);
    }
    return 0;
}


posted @ 2020-05-23 22:56  优秀的渣渣禹  阅读(183)  评论(0编辑  收藏  举报