dp1~10
$t1.P2704 $ 炮兵阵地(状压)** *再看一遍
由数据范围易知,状压,每个炮兵会影响前两行,所以dp状态里要带前一行的状态和当前行状态,不考虑前两行是因为转移是,确定前一行合法,前两行会被当做前一行的前一行
设\(dp[L][S][i]\)表示当前在第\(i\)行,当前行状态为\(S\),上一行状态为\(L\),
\(dp[L][S][i]=max(dp[L][S][i],dp[last~L][L][i-1]+sum[S])\)
判断山丘,令平原为\(0\),山丘为\(1\),\(S\&a[i]\)不为\(0\),不合法
判断左右两个距离在两格之内
\(S\&(S<<1)\)和\(S\&(S<<2)\) 不为0,不合法
判断每一列之前两行
\(S\&L\)和\(S\&last~L\) 不为\(0\)不合法
代码不粘
\(t2.\)排兵布阵
背包模型,城堡看为背包,兵力看为代价,获胜场数*第几个城堡为价值
\(f[j]\)表示已经派出\(j\)兵力的最大价值,枚举城堡\(i\),将每个人派出的兵力排序后,记第\(k\)个人派出的兵力为\(a[i][k]\),由于已经排好序,能打败第\(k\)个敌人就能打败第\(k-1\)个,因此枚举敌人\(k\),贪心地派出刚好比他的兵力两倍多\(1\)的兵力转移
\(f[j]=\max(f[j],f[j-(2*a[i][k]+1)]+i*k)\)
int main(){
read(S); read(n); read(m);
int i,j,k;
for(i = 1;i <= S;++i)
for(j = 1;j <= n;++j)
read(a[j][i]);
for(i = 1;i <= n;++i)
sort(a[i] + 1,a[i] + 1 + S);
for(i = 1;i <= n;++i)
for(j = m;j >= 0;--j)
for(k = 1;k <= S;++k)
if(j > 2*a[i][k])
f[j] = max(f[j],f[j - 2*a[i][k] -1] + i*k);
printf("%d",f[m]);
}
\(t3.\)病毒感染
这是绿题吗???,可以评个蓝紫题 另外\(Orz\) \(SyadouHayami\)神仙
可以发现往左走就要把左边所有没治愈的村庄治愈
过程可以分为几段,每段以任意顺序治愈后回到该段起点,再治愈其它段
设\(f_i\)表示治愈前\(i\)个村庄最小死亡人数,\(s_i=\sum\limits_{j=1}^i a_i\),\(w_{i,j}\)为从\(i\)出发治愈\([i,j]\)所有村庄再返回\(i\)的最小死亡人数,转移方程为
\(f_0=0\)
\(w_{i,j}\)从\(w_{i+1,j}\)转移,枚举\(i\)开始治不治
\(w_{i,i}=s_n-s_i\)
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]),s[i] = s[i-1] + a[i];
for (int j = 1;j <= n;++j) {
w[j][j] = s[n]-s[j];
for (int i = j-1;i;--i)
w[i][j] = w[i+1][j] + min(2*(s[n]-s[i]) + s[n]-s[j],3*a[i]*(j-i) + 2*(s[n]-s[j]) + s[n]-s[i]);
}
memset(f,0x3f,sizeof(f)),f[0]=0;
for(int i = 1;i <= n;++i)
for(int j = 0;j < i;++j)
f[i] = min(f[i],f[j] + (j != 0)*(s[n]-s[j]) + w[j+1][i] + (i-j-1)*(s[n]-s[i]));
printf("%lld\n",f[n]);
}
\(t4\) \(Game~~on~~a~~Tree\)
题意:
一张无向图, \(Alice\)选择从某处开始放一个棋子,然后 \(Bob\) 和 \(Alice\) 依次移动这个棋子,但是不能走到到过的地方,无法操作者败。
假如做过P1623,可能会联想到要找树的匹配
分类讨论
假如树是完美匹配的,后手只要走先手的完美匹配点即可,先手会失败
树没有完美匹配,找出树的最大匹配\((1,2),(3,4)\),假如先手从一个没在最大匹配的点开始,后手不可能再走到一个没有在最大匹配的点,先后手转换
令\(f_x\)表示\(x\)子树中多少点没有匹配即可
\(令cnt=\sum_{以i为根的后代}\) \(f_i=cnt-1(cnt>0)\) 或 \(f_i=1(cnt=0)\)
#include<cstdio>
#include<iostream>
using namespace std;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
const int N = 1e5 + 10;
struct edge{int to,next;}e[N<<1]; int head[N],tot,f[N];
inline void add(int u,int v){
e[++tot] = (edge){v,head[u]}; head[u] = tot;
}
void dfs(int x,int fa){
f[x] = -1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(v == fa) continue;
dfs(v,x); f[x] += f[v];
}
if(f[x] < 0) f[x] = -f[x];
}
int main(){
int n;scanf("%d",&n);int u,v;
for(int i = 1;i < n;++i){
u = read(); v = read();
add(v,u); add(u,v);
} dfs(1,0);
puts(f[1] ? "Alice" : "Bob");
}
\(t5:\) 取石子游戏
\(Nim\)游戏:各堆石子异或和为\(0\),先手必败,否则必胜
如果第一堆石子数目大于其它石子异或和,先手可以拿走大于的数量,转为必胜态
所以要让第一堆石子数\(\le\)其它石子异或和
定义\(f[i][j]\)为前\(i\)堆石子\(xor\)和为\(j\)的方案数,注意转移需要跳过枚举的第一堆石子,异或和最大为256
\(O(n^3)\)
int f[205][260],a[205],n,ans;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
f[0][0] = 1;
for(int i = 1;i <= n;++i){
for(int j = 1;j <= n;++j)
for(int k = 0;k < 256;++k){
if(i == j) f[j][k] = f[j-1][k];//跳过
else f[j][k] = (f[j-1][k] + f[j-1][k^a[j]]) % mod;
}
for(int j = a[i];j <= 255;++j) ans += f[n][j],ans %= mod;
}
printf("%d",ans);
}