基础DP
POJ 3176: Cow Bowling
数字三角形问题,DP方程不再赘述。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
数字三角形问题,DP方程不再赘述。
*/
#include
#include
#include
#include
#include
#include
POJ 2229: Sumsets
题意:给出一个整数n,求解该整数n有多少种由2的幂次之和组成的方案。
法一可以直接分析出递推式。
解题思路:
1.可以将n用二进制表示。
n=1,只有1种表示方法。
n=2,10(2),二进制表示下,可以分拆成{1,1},{10}有两种表示方法。
n=3, 11(2),可以分拆成{1,1,1},{10,1}。
n=4, 100(2),{1,1,1,1},{10,1,1},{10,10},{100}。
总结:如果所求的n为奇数,那么所求的分解结果中必含有1,因此,直接将n-1的分拆结果中添加一个1即可 为s[n-1]。
如果所求的n为偶数,那么n的分解结果分两种情况
1.含有1 这种情况可以直接在n-1的分解结果中添加一个1即可,s[n-1]。
2.不含有1 那么,分解因子的都是偶数,将每个分解的因子都除以2,刚好是n/2的分解结果,并且可以与之一一对应,这种情况有s[n/2]。
法二用完全背包实现。
为了避免1+2+1和1+1+2这样的重复计数。
外层从小到大循环物品(这样小物品在前尝试,大物品在后尝试),内层正序循环则保证了完全背包。
代码如下
/*
题意:给出一个整数n,求解该整数n有多少种由2的幂次之和组成的方案.
*/
#define method_1
#ifdef method_1
/*
法一可以直接分析出递推式。
解题思路:
1.可以将n用二进制表示。
n=1,只有1种表示方法。
n=2,10(2),二进制表示下,可以分拆成{1,1},{10}有两种表示方法。
n=3, 11(2),可以分拆成{1,1,1},{10,1}。
n=4, 100(2),{1,1,1,1},{10,1,1},{10,10},{100}。
总结:如果所求的n为奇数,那么所求的分解结果中必含有1,因此,直接将n-1的分拆结果中添加一个1即可 为s[n-1]。
如果所求的n为偶数,那么n的分解结果分两种情况
1.含有1 这种情况可以直接在n-1的分解结果中添加一个1即可,s[n-1]。
2.不含有1 那么,分解因子的都是偶数,将每个分解的因子都除以2,刚好是n/2的分解结果,并且可以与之一一对应,这种情况有s[n/2]。
*/
#include
#include
#include
#include
#include
#include
POJ 2385: Apple Catching
d[i,j]表示到第i分钟,移动了j次后获得的的最多苹果数。显然根据j的奇偶性可以直接知道bessie所在的苹果树。
初值:。其他也都为0。
目标:。
转移方程:每一阶段都有两种选择,一种是移动,一种是不移动。如果要判断i时刻移动了j次后是否能接到苹果,只要用即可。
即
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
d[i,j]表示到第i分钟,移动了j次后获得的的最多苹果数。显然根据j的奇偶性可以直接知道bessie所在的苹果树。
初值:d[0,0]=0。其他也都为0。
目标:max{d[t,i]},0<=i<=w。
转移方程:每一阶段都有两种选择,一种是移动,一种是不移动。如果要判断i时刻移动了j次后是否能接到苹果,只要用(j&1)==(a[i]-1))即可。
即d[i,j]=max{d[i-1,j-1],d[i-1,j]+(j&1)==(a[i]-1))}
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxt=1000+5;
const int maxw=30+5;
const int INF=0x3f3f3f3f;
int d[maxt][maxw],t,w,a[maxt],ans;
int main() {
ios::sync_with_stdio(false);
// freopen("Apple Catching.in","r",stdin);
cin>>t>>w;
for(int i=1;i<=t;i++) cin>>a[i],a[i]--;
for(int i=1;i<=t;i++){
for(int j=0;j<=w;j++){
if(j>=1) d[i][j]=max(d[i-1][j-1],d[i-1][j])+((j&1)==a[i]);
else d[i][j]=d[i-1][j]+((j&1)==a[i]);
}
}
for(int i=0;i<=w;i++) ans=max(ans,d[t][i]);
cout<
POJ 3616: Milking Time
法一 对于每个区间[l,r]建边l->r+R。然后就在相邻的区间间建边,边权为0,保证顶点编号递增即可。
为了便于计算,额外建边0->第一个区间左端点和 最后一个区间右端点->n+r。
起点为0,终点n+r,跑最长路即可。
法二 将区间按照左端点升序排序,就是一个LIS问题。
代码如下
/*
*/
#define method_2
#ifdef method_1
/*
对于每个区间[l,r]建边l->r+R。然后就在相邻的区间间建边,边权为0,保证顶点编号递增即可。
为了便于计算,额外建边0->第一个区间左端点和 最后一个区间右端点->n+r。
起点为0,终点n+r,跑最长路即可。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000*2+5;
const int maxm=1000+5;
const int INF=0x3f3f3f3f;
int n,m,r,vis[maxn],d[maxn],head[maxn],tot=1;
struct node{
int from,to,c;
}edge[maxm<<2];
void add(int from,int to,int c){
edge[++tot].from=head[from],head[from]=tot,edge[tot].c=c,edge[tot].to=to;
}
queueq;
void spfa(){
// memset(d,INF,sizeof(d));
memset(vis,0,sizeof(vis));
d[0]=0;q.push(0);vis[0]=1;
while(q.size()){
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=edge[i].from){
int y=edge[i].to,c=edge[i].c;
if(d[x]+c>=d[y]){
d[y]=d[x]+c;
if(!vis[y]){
q.push(y);
vis[y]=1;
}
}
}
}
// for(int i=0;i<=n+r;i++) D(d[i]);
}
int main() {
ios::sync_with_stdio(false);
//freopen("Milking Time.in","r",stdin);
cin>>n>>m>>r;
int u,v,c;
for(int i=1;i<=m;i++){
cin>>u>>v>>c;add(u,v+r,c);vis[u]=vis[v+r]=1;
}
u=1,v=0;
while(1){
while(!vis[u]&&u=n+r) break;
v=u,u++;
}
spfa();
cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000*2+5;
const int maxm=1000+5;
const int INF=0x3f3f3f3f;
int n,m,r,d[maxm],ans=0;
struct node{
int s,t,c;
bool operator<(const node &h)const{return s>n>>m>>r;
int u,v,c;
for(int i=1;i<=m;i++){
cin>>seg[i].s>>seg[i].t>>seg[i].c;
}
sort(seg+1,seg+m+1);
// for(int i=1;i<=m;i++) d[i]=seg[i].c;
for(int i=1;i<=m;i++){
d[i]=seg[i].c;
for(int j=1;j<=i-1;j++){
if(seg[j].t+r<=seg[i].s) d[i]=max(d[i],d[j]+seg[i].c);
}
ans=max(ans,d[i]);
}
cout<
POJ 3280: Cheapest Palindrome
d[i,j]表示将[i,j]变成回文串的最小代价。
初值:d[i,i]=0,其他为INF。
目标:d[1,m]。
转移方程:
1,若a[i]==a[j],d[i,j]=d[i+1,j-1]
2,若a[i]!=a[j],d[i,j]=min{d[i+1,j]+in[a[i]],d[i+1,j]+del[a[i]],d[i,j-1]+in[a[j],d[i,j-1]+del[a[j]]}
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
d[i,j]表示将[i,j]变成回文串的最小代价。
初值:d[i,i]=0,其他为INF。
目标:d[1,m]。
转移方程:
1,若a[i]==a[j],d[i,j]=d[i+1,j-1]
2,若a[i]!=a[j],d[i,j]=min{d[i+1,j]+in[a[i]],d[i+1,j]+del[a[i]],d[i,j-1]+in[a[j],d[i,j-1]+del[a[j]]}
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=26+5;
const int maxm=2000+5;
const int INF=0x3f3f3f3f;
int n,m,d[maxm][maxm];
string a;
mapin,del;
void dp(){
memset(d,INF,sizeof(d));
for(int i=1;i<=m;i++) d[i][i]=d[i][i-1]=0;
for(int len=2;len<=m;len++){
for(int i=1;i<=m-len+1;i++){
int j=i+len-1;
// D(i);D(j);E;
if(a[i]==a[j]) d[i][j]=d[i+1][j-1];
else{
int temp1=min(in[a[i]],del[a[i]]);
d[i][j]=min(d[i+1][j]+temp1,d[i][j]);
int temp2=min(in[a[j]],del[a[j]]);
d[i][j]=min(d[i][j-1]+temp2,d[i][j]);
}
}
}
}
int main() {
ios::sync_with_stdio(false);
//freopen("Cheapest Palindrome.in","r",stdin);
cin>>n>>m;
cin>>a;a.insert(0,"0");
char ch;int c1,c2;
for(int i=1;i<=n;i++){
cin>>ch>>c1>>c2;
in[ch]=c1,del[ch]=c2;
}
dp();
/*
for(int i=1;i<=m;i++){
for(int j=i;j<=m;j++){
D(d[i][j]);
}
E;
}
*/
cout<
优化递推式
POJ 1742: Coins
f[i]表示组成i元是否可行。
初值:f[0]=1
目标:所有f[i]=1的个数
转移方程:
设used[j]表示F[j]在阶段i时为1至少需要用多少枚第i种硬币。
若f[j-A[i]]=1,且used[j=A[i]]
/*
*/
#define method_1
#ifdef method_1
/*
f[i]表示组成i元是否可行。
初值:f[0]=1
目标:所有f[i]=1的个数
转移方程:
设used[j]表示F[j]在阶段i时为1至少需要用多少枚第i种硬币。
若f[j-A[i]]=1,且used[j=A[i]]
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const int maxm=100000+5;
const int INF=0x3f3f3f3f;
int f[maxm],used[maxm],n,m,A[maxn],C[maxn],ans;
void init(){
memset(f,0,sizeof(f));f[0]=1;ans=0;
}
int main() {
ios::sync_with_stdio(false);
//freopen("Coins.in","r",stdin);
while(cin>>n>>m){
init();
if(n==0&&m==0) break;
for(int i=1;i<=n;i++) cin>>A[i];
for(int i=1;i<=n;i++) cin>>C[i];
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
for(int j=1;j<=m;j++){
if(!f[j]&&j-A[i]>=0){
if(f[j-A[i]]==1&&used[j-A[i]]
POJ 3046: Ant Counting
法一,多重集组合数。
f[i,j]表示前i种蚂蚁,选出j个的方案数。直接跑多重背包+滚动数组。600msAC。
转移方程:
法二,对method_1的转移方程优化。
,同时注意出现负数的情况即可。
100msAC。
代码如下
/*
*/
#define method_2
#ifdef method_1
/*
多重集组合数。
f[i,j]表示前i种蚂蚁,选出j个的方案数。直接跑多重背包+滚动数组。600msAC。
转移方程:f[i,j]=∑f[i-1,j-k],0<=k<=min(j,num[i])
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxa=100000+5;
const int INF=0x3f3f3f3f;
const int mod=1e6;
int t,a,s,b,num[maxn],f[2][maxa],tot=0;
int main() {
ios::sync_with_stdio(false);
//freopen("Ant Counting.in","r",stdin);
cin>>t>>a>>s>>b;
int temp;
for(int i=1;i<=a;i++){
cin>>temp;num[temp]++;
}
f[0][0]=1;
for(int i=1;i<=t;i++){
memset(f[i&1],0,sizeof(f[i&1])); //滚动数组这里要先清空
tot+=num[i];
for(int j=0;j<=tot;j++){ //卡常数方式 这里将tot改成a就会TLE
for(int k=0;k<=min(j,num[i]);k++){
f[i&1][j]=(f[i&1][j]+f[i-1&1][j-k])%mod;
}
}
}
int ans=0;
for(int i=s;i<=b;i++) ans=(ans+f[t&1][i])%mod;
cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxa=100000+5;
const int INF=0x3f3f3f3f;
const int mod=1e6;
int t,a,s,b,num[maxn],f[2][maxa];
int main() {
ios::sync_with_stdio(false);
//freopen("Ant Counting.in","r",stdin);
cin>>t>>a>>s>>b;
int temp;
for(int i=1;i<=a;i++){
cin>>temp;num[temp]++;
}
f[0][0]=f[1][0]=1; //这个转移方程中f[1,1]可能来自f[0,0],f[0,1](一定为0),f[1,0]
for(int i=1;i<=t;i++){
// memset(f[i&1],0,sizeof(f[i&1])); //同理 滚动数组这里不要清空
for(int j=1;j<=a;j++){
if(j-1-num[i]>=0) f[i&1][j]=f[i&1][j-1]-f[i-1&1][j-1-num[i]]+f[i-1&1][j];
else f[i&1][j]=f[i-1&1][j]+f[i&1][j-1];
f[i&1][j]=(f[i&1][j]+mod)%mod; //防止负数
}
}
int ans=0;
for(int i=s;i<=b;i++) ans=(ans+f[t&1][i])%mod;
cout<
POJ 3181: Dollar Dayz
完全背包即可。
但要高精度。
method_1结果为WA。
代码如下
/*
*/
#define method_2
#ifdef method_1
/*
要高精度。
method_1结果为WA。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxk=100+5;
const int INF=0x3f3f3f3f;
int d[maxn],n,k;
int main() {
ios::sync_with_stdio(false);
//freopen("Dollar Dayz.in","r",stdin);
cin>>n>>k;
d[0]=1;
for(int i=1;i<=k;i++) for(int j=i;j<=n;j++) d[j]+=d[j-i];
cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxp=100+5;
const int INF=0x3f3f3f3f;
int n,k;
struct bigint{
int a[maxp],n;
int &operator[](int x){return a[x];}
bigint(){for(int i=0;i=1;i--)printf("%d",a[i]);}
friend bigint operator+(bigint x,bigint y){
bigint res;res.n=x.n>y.n?x.n:y.n; int i;
for(i=1;i<=res.n;i++)res[i]=x[i]+y[i];
for(i=1;i<=res.n;i++)res[i+1]+=res[i]/10,res[i]%=10;
if(res[res.n+1])res.n++; return res;
}
}d[maxn];
int main() {
ios::sync_with_stdio(false);
//freopen("Dollar Dayz.in","r",stdin);
cin>>n>>k;
/*
d[0]=1;
for(int i=1;i<=k;i++) for(int j=i;j<=n;j++) d[j]+=d[j-i];
cout<
进阶DP
POJ 1065: Wooden Sticks
Dilworth定理
https://blog.csdn.net/litble/article/details/85305561
因此本题就是按l升序排序,若l相同则按w升序排序。然后对w求最长严格下降子序列(w1<=w2的反面是w1>w2)的长度
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
Dilworth定理
https://blog.csdn.net/litble/article/details/85305561
因此本题就是按l升序排序,若l相同则按w升序排序。然后对w求最长严格下降子序列(w1<=w2的反面是w1>w2)的长度
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=5000+5;
const int INF=0x3f3f3f3f;
int T,n,d[maxn],ans;
struct node{
int l,w;
bool operator<(const node& h)const{return (l>T;
while(T--){
cin>>n;
init(n);
for(int i=1;i<=n;i++) cin>>seg[i].l>>seg[i].w;
sort(seg+1,seg+n+1);
for(int i=1;i<=n;i++) for(int j=1;j
POJ 1631: Bridging signals
和Wooden Sticks原理类似,不过由于数据量较大,所以dp时要用nlogn的算法。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
和Wooden Sticks原理一样,不过由于数据量较大,所以dp时要用nlogn的算法。
*/
#include
#include
#include
const int maxn=40000+10;
int n,T;
int d[maxn];//d[i]表示1~i中最长的不下降子序列的长度
struct node {
int a,b;
} qj[maxn];
bool operator<(const node &a1,const node &a2) {
if(a1.a!=a2.a) return a1.aans) { //插在了最后 说明答案可以更新
ans++;
}
// d[i]=std::max(temp+1,d[i]);
}
// for(int i=1;i<=n;i++){
// std::cout<
POJ 3666: Making the Grade
题解链接https://www.jianshu.com/p/2c07da54278b
代码如下
/*
https://www.jianshu.com/p/2c07da54278b
*/
#define method_2
#ifdef method_1
/*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=2000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],b[maxn],f[maxn][maxn],cnt;
int main() {
ios::sync_with_stdio(false);
//freopen("Making the Grade.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;
/*
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
}
*/
int ans=INF;
memset(f,INF,sizeof(f));
for(int i=1;i<=cnt;i++) f[0][i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=cnt;j++){
for(int k=1;k<=j;k++){
f[i][j]=min(f[i-1][k]+abs(a[i]-b[j]),f[i][j]);
}
if(i==n) ans=min(ans,f[n][j]);
}
}
cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=2000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],b[maxn],f[maxn][maxn],cnt;
int main() {
ios::sync_with_stdio(false);
//freopen("Making the Grade.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;
/*
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
}
*/
int ans=INF;
memset(f,INF,sizeof(f));
for(int i=0;i<=cnt;i++) f[0][i]=0;
for(int i=1;i<=n;i++){
int val=f[i-1][0];
for(int j=1;j<=cnt;j++){
val=min(val,f[i-1][j]);
f[i][j]=val+abs(a[i]-b[j]);
if(i==n) ans=min(ans,f[n][j]);
}
}
cout<
POJ 2392: Space Elevator
这道题因为每个点的限制不一样,所以需要从限制小的先进行dp(按照a[i]升序排序后dp)。
因为限制大的可以放在限制小的上面,但是限制小的无法再往大的上面堆。
f[i,j]表示使用前i个block,能否达到高度j。
初值:
目标:条件:,输出最大的满足条件的i
转移方程:
这个多重背包在实现时,可以省略f数组的第一维。同时由于c[i]范围很小,所以不用二进制或者单调队列优化。
代码如下
/*
*/
#define method_1
#ifdef method_1
/*
这道题因为每个点的限制不一样,所以需要从限制小的先进行dp(按照a[i]升序排序后dp)。
因为限制大的可以放在限制小的上面,但是限制小的无法再往大的上面堆。
f[i,j]表示使用前i个block,能否达到高度j。
初值:f[0,0]=1
目标:条件:f[n,i]=1,输出最大的满足条件的i
转移方程:f[i,j]|=f[i-1,j-k*h[i]] j<=a[i],0<=k<=c[i]
这个多重背包在实现时,可以省略f数组的第一维。同时由于c[i]范围很小,所以不用二进制或者单调队列优化。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxk=400+5;
const int maxai=40000+5;
const int INF=0x3f3f3f3f;
int f[maxai],h[maxk],a[maxk],c[maxk],k;
struct node{
int h,a,c;
bool operator<(const node& h)const{return a>k;
for(int i=1;i<=k;i++) cin>>block[i].h>>block[i].a>>block[i].c;
sort(block+1,block+k+1);
f[0]=1;
for(int i=1;i<=k;i++){
for(int j=1;j<=block[i].c;j++){ //循环数量放在第二重循环里头
for(int l=block[i].a;l>=block[i].h;l--){
f[l]|=f[l-block[i].h]; //注意这里不是f[l-j*block[i].h]
}
}
}
for(int i=maxai-5;i>=0;i--){
if(f[i]){
cout<
POJ 2184: Cow Exhibition
01背包。
d[i,j]表示前i个奶牛,TS为j时最大的TF。(第一维可省略,第二维需要数组下标平移)
如果S是负值的话会导致重复计算,所以此时需要调转循环方向。
PS:这题深搜可过。只需要加两个剪枝即可。
剪枝一:s和f都大于等于0必选。
剪枝二:s和f都小于0必不选。
代码如下
/*
PS:这题深搜可过。只需要加两个剪枝即可。
剪枝一:s和f都大于等于0必选。
剪枝一:s和f都小于0必不选。
*/
#define method_1
#ifdef method_1
/*
01背包。
d[i,j]表示前i个奶牛,TS为j时最大的TF。(第一维可省略,第二维需要数组下标平移)
如果S是负值的话会导致重复计算,所以此时需要调转循环方向。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const int base=100*1000+5;
const int INF=0x3f3f3f3f;
int n,s[maxn],f[maxn],d[base*2+1005],ans=0;
int main() {
ios::sync_with_stdio(false);
//freopen("Cow Exhibition.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i]>>f[i];
memset(d,-INF,sizeof(d));d[base]=0;
for(int i=1;i<=n;i++){
if(s[i]>=0){
//本来应该在[-base+s[i],base]之间dp,坐标平移后就变成了[s[i],base]
for(int j=base*2;j>=s[i];j--) d[j]=max(d[j],d[j-s[i]]+f[i]);
}
else{
for(int j=s[i];j<=base*2+s[i];j++) //注意这里不是for(int j=s[i];j<=base*2;j++) 这样j-s[i]就会到达不合法的区域
d[j]=max(d[j],d[j-s[i]]+f[i]);
}
}
for(int i=base;i<=base*2;i++){ //只有[base,base*2]是合法的
if(d[i]>=0) ans=max(ans,d[i]+i-base);
}
cout<