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=sumlimits_{j=1}^i a_i)(w_{i,j})为从(i)出发治愈([i,j])所有村庄再返回(i)的最小死亡人数,转移方程为

[f_i=minlimits_{j<i}{f_j+[j ot=0](s_n-s_j)+w_{j+1,i}+[i ot=n](i-j-1)(s_n-s_i)} ]

(f_0=0)

(w_{i,j})(w_{i+1,j})转移,枚举(i)开始治不治

[w_{i,j}=w_{i+1,j}+min egin{cases} 2(s_n-s_i)+(s_n-s_j)\ 3(j-i)a_i+(s_n-s_i)+2(s_n-s_j)\ end{cases} ]

(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
",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);
}
原文地址:https://www.cnblogs.com/shikeyu/p/13778449.html