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_i=\min\limits_{j<i}\{f_j+[j\not=0](s_n-s_j)+w_{j+1,i}+[i\not=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 \begin{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\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);
}
posted @ 2020-10-07 18:59  INFP  阅读(102)  评论(0编辑  收藏  举报