归纳总结一下吧,不然学了就忘
目前接触过四种题型,
dp[ i ][ state ] = sigma( dp[i-1] [ stateB ] )
dp[i] [state] = max( dp[i-1] [stateB ]) 遍历i-1的stateB
state 可以推出 nstate
dp[ nstate ] = max( dp[nstate] , dp[state] + 代价) , 从当前state可以逐个分析已经获得的信息。
dp[ state ] [ u ] 表示已完成的图的状态,已经当前点在u上 的最小代价
倘若 u -> v
dp [ state | (1 < < v) ][ v ] = max ( dp [ state | (1 < < v) ] [v], dp[ state ] + mp[[u] [v] )
属于套路3
给定吕布攻击力、防御力、生命,给定n个敌人的攻击力、防御力、生命值、可得经验。
升级可以增加攻击力、防御力、生命。
问选择杀敌顺序使得所有怪物全部杀掉,而且剩余的生命值最大
养成DP思维,我们有2^n 种杀敌顺序,利用state [0 ,1 < < n ) ,可以把所以的顺序表示出来,这个时候可能会有疑问,000110, 表示杀了第二个敌人和第三个敌人,可是先杀哪个是和结果相关的,比如经验涨的就不一样,怎么思考这点? 我们要知道 ,000110 ,是由 000100 和 000010 推得到的,因此DP会在两条路径里选择最优。因此 111111111 表示的就是杀完所有怪物的最优解。
还说一个,当前杀敌状态,是可以算出等级的,因此也可以算出升级完的状态。
再说一个,吕布砍敌人t次才能使敌人死, 那他自己要承受 t-1 敌人的攻击。
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 25;
const int MOD = 998244353;
struct Guai
{
int ati,def,hp,exp;
} g[maxn];
int dp[ 1 << 20 ];
int main()
{
int ati,def,hp;
int inati,indef,inhp;
while(scanf("%d%d%d%d%d%d",&ati,&def,&hp,&inati,&indef,&inhp) != EOF)
{
memset(dp,0,sizeof(dp));
int n;
scanf("%d",&n);
for(int i=0; i>st;
scanf("%d%d%d%d",&g[i].ati,&g[i].def,&g[i].hp,&g[i].exp);
}
dp[0] = hp;
for(int state = 0; state < (1<state ++) // 杀敌状态
{
if(dp[state] <= 0)
continue;
// 构造出 当前吕布状态
int lvLevel = 1, nowExp = 0;
for(int j=0; jif(state & (1<exp;
}
lvLevel = nowExp / 100;
int ATI = ati + lvLevel * inati;
int DEF = def + lvLevel * indef;
lvLevel++;
// cout<<"state: "<<state <<"level: "<for(int i=0; iif( (state & (1 << i) )> 0)
continue;//杀过了
int hurtLv = max(1, ATI - g[i].def);
int hurtG = max(1, g[i].ati -DEF);
int timLv =ceil(g[i].hp*1.0 / hurtLv );
int lose = (timLv-1) * hurtG;
if(lose >= dp[state])
continue;
//cout<<"吕布出招次数: "<" 第 "<" 号怪兽出招: "<if(nowExp + g[i].exp >= lvLevel * 100)
dp[state|(1<state|(1<state] - ( (timLv-1) * hurtG) + inhp);
else
dp[state|(1<state|(1<state] - ( (timLv-1) * hurtG));
}
}
if(dp[(1<1] <= 0)
printf("Poor LvBu,his period was gone.\n");
else
printf("%d\n",dp[(1<1]);
}
return 0;
}
按套路走,本题属于第一类问图种类数。
本题难点在于,当前状态和上一个状态如何算兼容。
现在讨论 (i,j)这个位置,
0 表示 这个位置摆放竖砖
1 表示这个位置摆放横砖,或者是左边横转的残余,或者是i -1 行的竖砖的残余
不兼容的情况有以下几种
1) (i j) 为0 , 若 (i -1 , j) 为0 ,那么不兼容,因为i - 1为0 意味着拜访了竖砖。
2 (i, j ) 为1, 1 可以表示很多情况, 只有这一种会出现不符合,
当前位置作为横铺的起点, 但是这个位置处于最末尾,那么他不可能拥有 横铺的残余
( i , j + 1) 必须是1 , (i - 1, j + 1) 也必须是 1。
当遇到0时, 操作完 i += 1 , 遇到 1 时,如果作为上一个竖铺的残余,那么 i += 1, 否则 i+=2。
这样保证了我们讨论的不会是横铺的残余位置
预处理一下第一行的合法状态,从第二行开始DP , 最终答案dp[ n ] [ 1 < < ( m ) - 1] ,最后一行不能有出现竖铺的起点
#include
#include
#include
#include
using namespace std;
#define MAX_ROW 11
#define MAX_STATUS 2048
int g_Width, g_Height;
bool FitFirstLine(int state)
{
int num = 0;
for(int i=0;iif( ((1< 0) num++;
else
{
if(num%2)
return false;
num = 0;
}
}
if(num%2)
return false;
// cout<
return true;
}
int FitLast(int stateA,int stateB)
{
int i=0;
while(iif ((stateA&(1<0)
{
if((stateB&(1<0)
return false;
i++;
}
else
{
if((stateB&(1<0)
i++;
else
{
if(i == g_Width-1 || !( stateA&(1<<(i+1)) && stateB&(1<<(i+1))))
return false;
i+=2;
}
}
}
return true;
}
long long dp[15][(1<<15)];
int main()
{
while(scanf("%d%d", &g_Height, &g_Width) != EOF )
{
if(g_Height == 0 && g_Width == 0) break;
if(g_Height < g_Width ) swap(g_Height,g_Width);
memset(dp,0,sizeof(dp));
for(int state = 0; state < (1<if(FitFirstLine(state))
dp[0][state] = 1; // 方案为1
}
for(int i=1;ifor(int k=0; k<(1<// 当前行的状态
{
for(int p=0; p<(1<// 上一行的状态
if(FitLast(k,p))
dp[i][k] += dp[i-1][p];
}
}
printf("%lld\n",dp[g_Height-1][(1<1]);
}
return 0;
}
这是第四类问题,旅行商
注意本题的要求,每个点至多去两次,意味着 1 2 2 1 。 2 2 2 2 ,这类无 0 的三进制数都是合法的。用三进制表示状态,我们思考一下相比二进制表示,哪些地方需要变动
假设状态是state
1、二进制下,我们探讨 第 i 个点,在当前状态下有无被访问过 (u -> v ,u必须出现,v必须为0),是用的 state&(1 << i) , 得到 1 即访问过,得到 0 为未访问。
三进制下,我们也要考虑当前状态state , 在节点u 上,经过了几次, 在节点v 上,经过了几次。
做法,因为state实际值是一个十进制,我们 把state / 3 得到的是 第 1 位的 次数, state / 9 得到 第二位的次数。 这里初始化处理 得到 dig[state][ i ] 数组表示状态state 在 i 位上的次数
2、二进制下,我们推下一个节点的状态,state | (1 < < v) , 这里从十进制去分析 ,假设v是第3位,那就是 state + pow(2,3) , 因此在三进制中, 我们也需要 state + pow(3,v)。
以上就是三进制下的状压,脑子笨没想出来,希望以后记牢
#include
#include
using namespace std;
const int maxn = 59050;
const int inf = 0x1f1f1f1f;
int dp[maxn][12];
int mp[12][12];
int dig[maxn][12];
int tri[] = {1,3,9,27,81,243,729,2187,6561,19683,59049 };
void init()
{
for(int i=1; i<59050; i++)
{
// 每一个状态
for(int t=i,k=0; k<12; k++)
{
dig[i][k] = t%3;
//cout<<"state: "<" dig: "<%3<3;
if(t == 0)
break;
}
}
}
int main()
{
int n,m;
init();
while(scanf("%d%d",&n,&m) == 2)
{
memset(dp,inf,sizeof(dp));
memset(mp,inf,sizeof(mp));
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a--;
b--;
mp[a][b] = mp[b][a] = min(c,mp[a][b]);
}
for(int i=0; i0;
}
int ans = inf;
for(int state = 1; state < tri[n]; state++) // state 三进制
{
int f = 1;
for(int i = 0; i < n; i++)
{
if(dig[state][i] == 0)
f = 0;
if(dp[state][i] == inf)
continue;
for(int j=0; j < n ; j++)
{
if(dig[state][j] >= 2 || i == j || mp[i][j] == inf)
continue;
int nstate = state + tri[j];
dp[nstate][j] = min(dp[nstate][j], dp[state][i] + mp[i][j]);
}
}
if(f)
for(int i=0; istate][i]);
}
if(ans >= inf)
ans = -1;
printf("%d\n",ans);
}
return 0;
}
没那么裸的状压DP
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1050;
typedef long long ll;
int cur[maxn];
char st[maxn];
int dp[maxn][1<<11], vis[1<<11];
int main()
{
int t;
cin >> t;
int cas = 1;
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)
{
scanf("%s",st+1);
cur[i] = 0;
for(int j=1; j<=m; j++)
{
if(st[j] == 'A')
cur[i] += (1<<(m-j));
}
}
printf("Case #%d: ",cas++);
if(n*(n-1)/2 printf("0\n");
else
{
memset(dp,0,sizeof(dp));
int high = 1<int ans = 0;
for(int state = 0; statememset(vis,0,sizeof(vis));
for(int j=1; j<=n; j++)
{
int andLast = state & cur[j];
dp[j][state] = dp[j-1][state] + (j-1) - vis[andLast];
vis[andLast]++;
}
}
for(int state = 0; state < high ; state++)
if(dp[n][state] >= k)
ans++;
printf("%d\n",ans);
}
}
return 0;
}
秒啊,题目开始越来越不裸了
具体代码在队友那,这是网上的刷表
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 15
#define INF 0x3f3f3f3f
#define LL long long
int a[N][N],fac[N];
int cnt[4500];
int dp[2][4500][505];
int gcd(int a,int b){
if (b==0) return a;
else return gcd(b,a%b);
}
void init(){
fac[0] = 1;
for (int i=1;i1]*i;
memset(cnt,0,sizeof(cnt));
for (int i=0;i<(1<<12);i++){
int u=i;
while (u){
if (u&1)
cnt[i]++;
u>>=1;
}
}
}
int main(){
int T;
scanf("%d",&T);
init();
while (T--){
int n,m;
scanf("%d%d",&n,&m);
for (int i=0;ifor (int j=0;jscanf("%d",&a[i][j]);
memset(dp,0,sizeof(dp));
for (int i=0;i0][1<0][i],m)]=1;
for (int i=1;iint now=i&1,last=(i-1)&1;
for (int j=0;j<(1<if (cnt[j]!=i)
continue;
for (int k=0;kif (j&(1<continue;
for (int l=0;l<=m;l++)
dp[now][j|(1<int fm=fac[n];
int fz=dp[(n-1)&1][(1<1][m];
if (fz==0){
printf("No solution\n");
}else{
int g=gcd(fm,fz);
printf("%d/%d\n",fm/g,fz/g);
}
}
return 0;
}