dp详解

  • 2.结语
  • Lmemset放在外面idonnot konw why

dp详解前语

A - POJ2342:Anniversary party

树形d水题。就是dp[1][0]表示不来那么子孙可以来可以不来。dp[1][1]表示来子孙不可以来,子孙的子孙可以来。所以在树上操作得到最后答案。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=6000+5;
int val[maxn],vis[maxn];
int dp[maxn][3];
vector<int>son[maxn];
int dfs(int i){
    dp[i][0]=0;
    dp[i][1]=val[i];
    for(int k=0;k<son[i].size();k++) {
        int sonz = son[i][k];
        dfs(sonz);
        dp[i][0] += max(dp[sonz][0], dp[sonz][1]);
        dp[i][1] += dp[sonz][0];
    }
}
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
        cin>>val[i];
    int x,y;
    for(int i=1;i<n;i++){
        cin>>x>>y;
        vis[x]++;
        son[y].push_back(x);
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            dfs(i);
            cout<<max(dp[i][0],dp[i][1])<<endl;
            return 0;
        }
    }
}

B - POJ1463 Strategic game

水题。某种方面就是把A改一下变min即可 B输入不用getchar。复杂输入用scanf 以下为ac代码 一开始输入格式有问题T了改一下输入就可。
#include<iostream>
#include <stdio.h>
#include <string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=6000+5;
int val[maxn],vis[maxn];
int dp[maxn][3];
vector<int>son[maxn];
int dfs(int i){
    dp[i][0]=0;
    dp[i][1]=1;
    for(int k=0;k<son[i].size();k++) {
        int sonz = son[i][k];
        dfs(sonz);
        dp[i][0] += dp[sonz][1];
        dp[i][1] += min(dp[sonz][0],dp[sonz][1]);;
    }
}
int main()
{
    int n;
    while(cin>>n) {
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        int x, y;
        for (int i = 0; i < n; i++) {
           int n1;
           scanf("%d:(%d)",&x,&n1);
            son[x].clear();
            while (n1--) {
                cin >> y;
                vis[y]++;
                son[x].push_back(y);
            }
        }
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                dfs(i);
                cout<<min(dp[i][0], dp[i][1]) << endl;
            }
        }
    }
}

错误输入


    while(cin>>n) {
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        int x, y;
        for (int i = 0; i < n; i++) {
            cin >> x;
            getchar();
            getchar();
            int n1;
            cin >> n1;
            getchar();
            son[x].clear();
            while (n1--) {
                cin >> y;
                vis[y]++;
                son[x].push_back(y);
            }
        }
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                dfs(i);
                cout<<min(dp[i][0], dp[i][1]) << endl;
            }
        }
    }

正确输入

 while(cin>>n) {
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        int x, y;
        for (int i = 0; i < n; i++) {
           int n1;
           scanf("%d:(%d)",&x,&n1);
            son[x].clear();
            while (n1--) {
                cin >> y;
                vis[y]++;
                son[x].push_back(y);
            }
        }
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                dfs(i);
                cout<<min(dp[i][0], dp[i][1]) << endl;
            }
        }
    }

C - POJ 2378.Tree Cutting

C有点复杂不是模板题。但是还是按照树的概念逐步更新关键在于从节点一步步更新设置更新的东西
#include <iostream>
using namespace std;
#include <vector>
int n;const int maxn=10100;
vector<int>edge[maxn];int f[maxn],dp[maxn];
void dfs(int u,int hui){
    f[u]=1;
    int maxx=-1;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v==hui)continue;
        dfs(v,u);
        f[u]+=f[v];
        maxx=max(maxx,f[v]);
    }
    dp[u]=max(maxx,n-f[u]);
}
int main()
{
    cin>>n;
    int x,y;
    int zyx=n/2;
    for(int i=1;i<n;i++){
        cin>>x>>y;
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs(1,-1);
    for(int i=1;i<=n;i++){
        if(dp[i]<=zyx){
            cout<<i<<endl;
        }
    }
}

P - hdu2196Computer

题解链接https://blog.csdn.net/shuangde800/article/details/9732825 思路正确但是代码不够简洁。S题的代码写得更好。不知道为啥上午总是思路不清理解缓慢。这个题花了一上午实在是不应该。简单来讲就是dfs1求每个结点作为根的子树最远距离。dfs2求答案 最远距离=max(f0,f1); f0=子树最远距离 f1=max(前层节点最大距离/第二长距离+dis(u,v),前层节点f1) 具体看题解懒得写。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;

typedef long long ll;

const int MAXN = 10010;
struct Node{
    int v, w;
};
vector<Node>edge[MAXN];
int n, m;
ll f[MAXN][2];
void dfs1(int u,int xback){
    f[u][0]=0;
    for(int i=0;i<edge[u].size();i++) {
        int v = edge[u][i].v;
        if(v==xback)continue;
        dfs1(v,u);
        f[u][0]=max(f[u][0],f[v][0]+edge[u][i].w);
    }
}
void dfs2(int u,int xback){
    int maxx=0,vx,secondmaxx=0,vs;
    int flag;
    if(u==2||u==3){
        flag=1;
    }
//    cout<<u<<" "<<<<endl;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i].v;
        int w=edge[u][i].w;
        if(v==xback)continue;
        int tmp=f[v][0]+w;
        if(tmp>maxx){
            //寻找最大的边长f[u][0]
            secondmaxx=maxx;vs=vx;//更新第二长secondmaxx
            maxx=tmp;vx=v;
//            if(flag)cout<<u<<maxx<<secondmaxx<<endl;
        }else if(tmp==maxx||tmp>secondmaxx){
            secondmaxx=tmp;vs=v;
//            if(flag)cout<<u<<secondmaxx<<endl;
        }

    }
    //寻找到最长边也就是f[u][0]也就是maxx最长的子节点vx=v和第二长secondmaxx
    if(u!=1) {//?
        int tmp = f[u][1];
        int v = -1;
        if (tmp > maxx) {
            secondmaxx = maxx;
            vs = vx;//更新第二长secondmaxx
            maxx = tmp;
            vx = v;
        } else if (tmp == maxx || tmp > secondmaxx) {
            secondmaxx = tmp;
            vs = v;
        }
    }
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i].v;
        int w=edge[u][i].w;
        if(v==xback)continue;
        if(v==vx){
            f[v][1]=w+secondmaxx;
        }
        else f[v][1]=w+maxx;
        dfs2(v,u);
    }
}
int main()
{
    int n;
    while(cin>>n) {
        for (int i = 1; i <= n; i++)edge[i].clear();
        int x, y;
        for (int i = 2; i <= n; i++) {
            cin >> x >> y;
            edge[x].push_back((Node) {i, y});
            edge[i].push_back((Node) {x, y});
        }
        dfs1(1, -1);
        dfs2(1, -1);
        for (int i = 1; i <= n; i++) {
            cout << max(f[i][0], f[i][1]) << endl;
        }
    }
}

S - POJ 4003 Bob’s Race

S又麻烦又有趣的一道题。简单来讲。求每个点的最大距离(两个dfs在p中写了)然后用rmq区间查询最值(由于这里要o1所以不用线段树用rmq)暴力求区间用查询得到响应值最后求最大值。其实就是两个dfs+rmq。这里给出由p过来的代码(自己写的)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;

typedef long long ll;
//#define a^j (a/2)<<j

const int MAXN = 50010;
struct Node{
    int v, w;
};
vector<Node>edge[MAXN];
int n, m;
ll f[MAXN][2];
ll d[MAXN];
int ma[MAXN][20], mi[MAXN][20];
void dfs1(int u,int xback){
    f[u][0]=0;
    for(int i=0;i<edge[u].size();i++) {
        int v = edge[u][i].v;
        if(v==xback)continue;
        dfs1(v,u);
        f[u][0]=max(f[u][0],f[v][0]+edge[u][i].w);
    }
}
void dfs2(int u,int xback){
    int maxx=0,vx,secondmaxx=0,vs;
    int flag;
    if(u==2||u==3){
        flag=1;
    }
//    cout<<u<<" "<<<<endl;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i].v;
        int w=edge[u][i].w;
        if(v==xback)continue;
        int tmp=f[v][0]+w;
        if(tmp>maxx){
            //寻找最大的边长f[u][0]
            secondmaxx=maxx;vs=vx;//更新第二长secondmaxx
            maxx=tmp;vx=v;
//            if(flag)cout<<u<<maxx<<secondmaxx<<endl;
        }else if(tmp==maxx||tmp>secondmaxx){
            secondmaxx=tmp;vs=v;
//            if(flag)cout<<u<<secondmaxx<<endl;
        }

    }
    //寻找到最长边也就是f[u][0]也就是maxx最长的子节点vx=v和第二长secondmaxx
    if(u!=1) {//?
        int tmp = f[u][1];
        int v = -1;
        if (tmp > maxx) {
            secondmaxx = maxx;
            vs = vx;//更新第二长secondmaxx
            maxx = tmp;
            vx = v;
        } else if (tmp == maxx || tmp > secondmaxx) {
            secondmaxx = tmp;
            vs = v;
        }
    }
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i].v;
        int w=edge[u][i].w;
        if(v==xback)continue;
        if(v==vx){
            f[v][1]=w+secondmaxx;
        }
        else f[v][1]=w+maxx;
        dfs2(v,u);
    }
}
int logg[MAXN];
int getlog(){
    for(int k=1;k<=n;k++) {
        logg[k]=0;
        while((1<<(logg[k]+1))<=k)logg[k]++;
//        int l = 2;
//        while (l <= k ) {
//            l *= 2;
//            logg[k]++;
//        }cout<<k<<logg[k]<<endl;
    }
}
void rmq_init(){
    for(int i=1;i<=n;i++){
        ma[i][0]=mi[i][0]=d[i];
    }
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++) {
            ma[i][j] = max(ma[i][j - 1], ma[i + (1<<(j - 1))][j - 1]);
            mi[i][j] = min(mi[i][j - 1], mi[i + (1<< (j - 1))][j - 1]);
        }
    getlog();
}
int rmq(int l,int r){
    int k=logg[r-l+1];
//    cout<<max(ma[l][k],ma[r-(2^k)+1][k])-min(mi[l][k],mi[r-(2^k)+1][k])<<endl;
    return max(ma[l][k],ma[r-(1 << k)+1][k])-min(mi[l][k],mi[r-(1 << k)+1][k]);
}
int main() {
    int query, Q;
    while (cin >> n >> query&&!(!n&&!query)) {
        for (int i = 1; i <= n; i++)edge[i].clear();
        int x, y, z;
        for (int i = 1; i < n; i++) {
            cin >> x >> y>>z;
            edge[x].push_back((Node) {y, z});
            edge[y].push_back((Node) {x, z});
        }
        dfs1(1, -1);
        dfs2(1, -1);
        for (int i = 1; i <= n; i++) {
            d[i] = max(f[i][0], f[i][1]);
        }
        rmq_init();

        while (query--) {
            cin >> Q;
            int l = 1, maxx = 0;
            for (int i = 1; i <= n; i++) {
                while (l < i && rmq(l, i) > Q)l++;
                maxx = max(maxx, i-l+ 1);
            }
            cout << maxx << endl;
        }
    }
}

别人的题解(这个写的真的优秀。可以直接去写p比原来的p的方法简单)https://www.cnblogs.com/gj-Acit/p/4661024.html

#pragma comment(linker, "/STACK:1677721600")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf (-((LL)1<<40))
#define lson k<<1, L, (L + R)>>1
#define rson k<<1|1,  ((L + R)>>1) + 1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FIN freopen("in.txt", "r", stdin)
#define FOUT freopen("out.txt", "w", stdout)
#define rep(i, a, b) for(int i = a; i <= b; i ++)
#define dec(i, a, b) for(int i = a; i >= b; i --)

template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }

//typedef __int64 LL;
typedef long long LL;
const int MAXN = 50000 + 100;
const int MAXM = 110000;
const double eps = 1e-8;
LL MOD = 1000000007;

struct Node {
    int v, s;
    Node(int _v = 0, int _s = 0) {
        v = _v; s = _s;
    }
};

vector<Node>G[MAXN];
int mx[MAXN], se[MAXN], d[MAXN], idx[MAXN];
int ma[MAXN][20], mi[MAXN][20];
int n, u, v, w;
int m, Q;

void init(int n) {
    rep (i, 0, n) G[i].clear();
    mem0(mx); mem0(se);
}

void dfs1(int u, int fa) {
    mx[u] = se[u] = 0;
    int sz = G[u].size();
    rep (i, 0, sz - 1) {
        int v = G[u][i].v;
        if(v == fa) continue;
        dfs1(v, u);
        int len = G[u][i].s + mx[v];
        if(len > mx[u]) se[u] = mx[u], mx[u] = len;
        else if(len > se[u]) se[u] = len;
    }
}

void dfs2(int u, int fa, int dis) {
    d[u] = max(dis, max(mx[u], se[u]));
    int sz = G[u].size();
    rep (i, 0, sz - 1) {
        int v = G[u][i].v, len = G[u][i].s;
        if(v == fa) continue;
        if(len + mx[v] == mx[u])
            dfs2(v, u, max(dis, se[u]) + len);
        else
            dfs2(v, u, max(dis, mx[u]) + len);
    }
}

void rmq_init(int n) {
    rep (i, 1, n) ma[i][0] = mi[i][0] = d[i];
    for(int j = 1; (1<<j) <= n; j ++) {
        for(int i = 1; i + (1<<j) - 1 <= n; i ++) {
            ma[i][j] = max(ma[i][j - 1], ma[i + (1 << (j - 1))][j - 1]);
            mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
        }
    }
    rep (len, 1, n) {
        idx[len] = 0;
        while((1 << (idx[len] + 1)) <= len) idx[len] ++;
    }
}

int rmq(int l, int r) {
    int k = idx[r - l + 1];
    return max(ma[l][k], ma[r - (1 << k) + 1][k]) - min(mi[l][k], mi[r - (1 << k) + 1][k]);
}

int main()
{
//    FIN;
    while(~scanf("%d %d", &n, &m) && n) {
        init(n);
        rep (i, 1, n - 1) {
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back(Node(v, w));
            G[v].push_back(Node(u, w));
        }

        //计算每个点到叶子节点的最远距离
        dfs1(1, -1);
        dfs2(1, -1, 0);

        //计算答案
        rmq_init(n);
        while(m --) {
            scanf("%d", &Q);
            int l = 1, ans = 0;
            rep (i, 1, n) {
                while(l < i && rmq(l, i) > Q) l ++;
                ans = max(ans, i - l + 1);
            }
            cout << ans << endl;
        }
    }
    return 0;
}

树形dp总结https://blog.csdn.net/qq_39304630/article/details/81836024

状压

更多题目移步状压专题https://blog.csdn.net/weixin_43331783/article/details/96566758

G -POJ - 2411Mondriaan's Dream

https://www.cnblogs.com/scau20110726/archive/2013/02/27/2935256.html 思路有点难想想出来就基本模拟了代码还好不算难写但最近真的疯狂写bug我也是醉了。周天这道题要再写一遍不要看题解自己改bug然后之前的bug是分析情况漏掉了j==m-1下次注意就这样。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define  mod 100000000
int n,m;
long long dp[15][1<<12];
bool firhang(int sta){
    for(int j=0;j<m;){
        if(sta&(1<<j)){
            if(j==m-1)return false;
            if(sta&(1<<(j+1))){
                j+=2;
            }
            else return false;
        }
        else j++;
    }
    return true;
}

//初始航{
//遍历列列举情况
//}
bool check(int now,int pre){
    for(int j=0;j<m;) {
        if (now & (1 << j)) {
            if ((pre & (1 << j))&&j!=m-1&& (pre & (1 << (j + 1))) && (now & (1 << (j + 1)))) {
                j += 2;
            } else if (!(pre & (1 << j)))j++;
            else return false;
        } else {
            if (pre & (1 << j))j++;
            else return false;
        }
    }
    return true;
}

//检查{
//遍历列列举情况
//}
void solve(){
    int li=1<<m;
    memset(dp,0, sizeof(dp));
for(int s=0;s<li;s++){
    if(firhang(s))
    dp[1][s]=1;
}
for(int i=2;i<=n;i++){
   for(int j=0;j<li;j++)
       for(int k=0;k<li;k++){
           if(check(j,k))
               dp[i][j]+=dp[i-1][k];
       }
}
cout<<dp[n][li-1]<<endl;
//遍历2-n行遍历jk行状态。检查增值。
//输出
}
int main()
{
    while(cin>>n>>m&&(n||m)){
        if((n&1)&&(m&1)){
            cout<<0<<endl;
            continue;
        }
        if(n<m){
            swap(n,m);
        }
        solve();
    }
    return 0;
}

H - Travelling HDU - 3001

https://blog.csdn.net/u014634338/article/details/50015825 HTSP问题有点难。感觉理解不是很透彻。独立不看题解再写一遍。 伪代码:(建议想清楚思路在看,否则只是会打没有) 1设置函数求状态的各个位置数和的12个数(three[],sum)返回个数。 2初始化dp、mp 3.设置函数(数组)求3的k次方(如果是二进制就2的) 4.建图(这里的点是1-n。两点之间可能有多个路径求最小) 5.重点:枚举状态{        枚举终点{        如果这个点有{        1如果是直接链接1(0)的点        2如果是不可大的        3当为最终状态(11111)        4枚举其他点(是他下一个点不是前一个!!!之前该bug改了好久)建立新状态(分有没有来过求最小值)        }        }    }    输出最小值(判断是否可达不可达为-1) 以下为代码
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
int mp[12][12],dp[12][60000];
int n,m;
const int maxn=0x3f3f3f3f;

int arr(int three[],int sum){
    int k=0;
    for(int i=0;i<n;i++){
        three[i]=sum%3;
        sum/=3;
        if(three[i])k++;
    }
//    cout<<sum<<k<<endl;
    return k;
}
void init(int li){
    for(int i=0;i<n;i++){
        for(int j=0;j<li;j++){
            dp[i][j]=-1;
        }
        for(int j=0;j<n;j++){
            mp[i][j]=-1;
        }
    }
}

int main()
{
    int a,b,c,in[12],three[12];
    in[0]=1;
    for(int i=1; i<=10; i++)in[i]=in[i-1]*3;
    while(~scanf("%d%d",&n,&m))
    {
        init(in[n]);
        for(int i=0;i<m;i++){
            cin>>a>>b>>c;
            a--;b--;
            if(mp[a][b]==-1){
                mp[a][b]=mp[b][a]=c;
            }
            else{
                mp[a][b]=mp[b][a]=min(c,mp[a][b]);
            }
        }
        int li=in[n],flag=-1,minn=maxn;
//        cout<<li<<endl;
        for(int s=1/**/;s<li;s++){
//            cout<<s<<"/"<<endl;
            int k=arr(three,s);
            for(int po=0;po<n;po++){
                if(three[po]){
                    if(k==1){dp[po][s]=0;}
                    if(dp[po][s]==-1)continue;
                    if(k==n){flag=0;minn=min(minn,dp[po][s]);}
                    for(int npo=0;npo<n;npo++){
                        if(npo!=po&&three[npo]<2&&mp[po][npo]!=-1){
                            int news=s+in[npo];
                            if(dp[npo][news]==-1)dp[npo][news]=dp[po][s]+mp[po][npo];
                            else dp[npo][news]=min(dp[npo][news],dp[po][s]+mp[po][npo]);
//                            cout<<po<<" "<<news<<" "<<dp[po][news]<<endl;
                        }
                    }
                }
            }
        }
        if(!flag)cout<<minn<<endl;
        else cout<<flag<<endl;
    }
    return 0;
}

https://blog.csdn.net/txgANG/article/details/72552055

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值