普及状压选做 (持更)

普及状压选做

目录
  • 第1题
  • 第2题
  • 第3题
  • 第4题
  • 第5题
  • 第6题


第1题

看了题解。
把点 \(n\) 作为不存在的第二起始点真是惊为天人的操作。(其实是我太菜啦

#include
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 13;

int n,m,v[N],g[N+1][N+1];
pairf[1<>q;while(q--) {
        memset(g,0,sizeof g);
        scanf("%d%d", &n,&m);
        rep(i,0,n-1)scanf("%d",&v[i]);
        rep(i,1,m) {int a,b; scanf("%d%d", &a,&b);
            --a;--b; g[a][b] = g[b][a] = 1;
        }
        memset(f,0x80,sizeof f);
        rep(i,0,n-1) f[1< ans = make_pair(0,0);
        
        rep(S,0,(1<>i)&1)) continue;
                int Si = S^(1<>j)&1)) continue;
                    int Sij = Si^(1<>k)&1)) continue;
                        int tmp = f[Si][j][k].first + v[i]*v[j] + (g[i][k]?v[i]*v[j]*v[k]:0);
                        if(f[S][i][j].first < tmp) {
                            f[S][i][j] = make_pair(tmp, f[Si][j][k].second);
                        } else if(f[S][i][j].first == tmp) {
                            f[S][i][j].second += f[Si][j][k].second;
                        }
                    }
                }
            }
        }
        
        rep(i,0,n-1)rep(j,0,n-1) {
            if(ans.first

第2题

似乎是轮廓线的状压
先上乘法原理, 每行的状态用 \(\{ 0,1 \}\) 表示, \(1\) 表示一个竖着的骨牌的上端, 其余为 \(0\)
转移似乎很难想啊……但发现一次转移无非考虑在本行怎么摆竖骨牌的上端。
配合预处理大法复杂度减少一个 \(O(n)\), 开心食用。
最后复杂度是 \(O(n*2^{m+1})\)

#include
using namespace std;
#define sub(i,k) for(int i=0;i<(1<<(k));++i)
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 11;


int n,m;
long long f[N+1][1<>j)&1) {
                    if(cnt&1) ok = false;
                    cnt=0;
                } else {
                    ++cnt;
                }
            }
            if(cnt&1) ok = false;
            good[i] = ok;
            // rep(j,0,m-1) cout<<((i>>j)&1);
            // cout<<' '<

第3题

经典题哟。
写了一下午自闭了。
换了个写法20min过啦。

#include
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
const int N = 100;
const int M = 10;

int n,m,a[N+1];
int cnt, nodes[61], sum[61];
long long f[2][61][61];

int main() {
    m = 10;
    scanf("%d%d", &n,&m);
    rep(i,1,n)rep(j,0,m-1) { char c;cin>>c;if(c=='H')a[i]|=(1<

第4题

题意就是求生成树, 因为最优解一定是原图的一个生成树。
刚开始想了个 sb做法, 妄想把每个点的深度信息塞到状态里, 后来发现这不就是爆搜 =_=。

参考题解, 用分层转移的手法, 转移的时候枚举当前生成树的子集作为最后一层, 然后转移, 注意要把当前集的树高塞进状态里。

转移的时候枚举当前集的子集, 将其作为前一状态。、但是无法保证枚举出来的就是 “最后一层”, 似乎可能会出现比最优解更小的解!但是可以冷静地想一下,由于转移总是合法的(即总是生成树), 所以得到的解一定 \(\ge\) 最优解, 而最优解一定会被找出, 所以正确性可以保证owo。

技巧总结: 二进制数表示集合时枚举某集合的子集的方法。
重大失误总结: 没有认识到极限数据(特别是小数据)对答案的影响。

#include
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)

const int N = 12;

int n,m,g[N][N],trans[1<

第5题

第6题

你可能感兴趣的:(普及状压选做 (持更))