• 状态压缩DP 不断学习中。。。。。。


    HDU 3001 travelling:http://acm.hdu.edu.cn/showproblem.php?pid=3001

    注意题目叙述: “But Mr Acmer gets bored so easily that he doesn't want to visit a city more than twice!”,

      题目大意:加限制的最小生成树,限制条件:每个节点最多只能到达两次, 用三进制(想想二进制表示的意思,可对比理解)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int N=20;
    const int ST=59049;
    const int INF=0x3f3f3f3f;
    
    int n,m;
    int g[N][N],dp[ST][N],ans;
    
    int bit[]={1,3,9,27,81,243,729,2187,6561,19683,59049}; //三进制初始化
    
    int Cal(int st){    //判断n条路是否全都走过
        for(int i=0;i<n;i++){
            if(st%3==0)
                return 0;
            st/=3;
        }
        return 1;
    }
    
    void Solve(){
        int i,j,k;
        ans=INF;
        for(i=0;i<bit[n];i++)
            for(j=0;j<n;j++)
                dp[i][j]=INF;
        for(i=0;i<n;i++)        //起点都为0
            dp[bit[i]][i]=0;
        for(i=0;i<bit[n];i++)
            for(j=0;j<n;j++)
                if(dp[i][j]!=INF){
                    int tmp=i;
                    for(k=0;k<n;k++){
                        if(tmp%3<2)     //不超过两次
                            dp[i+bit[k]][k]=min(dp[i+bit[k]][k],dp[i][j]+g[j][k]);
                        tmp/=3;
                    }
                    if(Cal(i))  //如果n条路都走过了,
                        ans=min(ans,dp[i][j]);
                }
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        while(~scanf("%d%d",&n,&m)){
            memset(g,0x3f,sizeof(g));
            int u,v,w;
            while(m--){
                scanf("%d%d%d",&u,&v,&w);
                u--;v--;
                if(g[u][v]>w)
                    g[u][v]=g[v][u]=w;
            }
            Solve();
            printf("%d\n",ans==INF?-1:ans);
        }
        return 0;
    }
    View Code

    HDU  1074  doing homework :  http://acm.hdu.edu.cn/showproblem.php?pid=1074

    思路:

    这题有作业数的规定,有每一样作业的截止时间要求,有写每一样作业所需花费的时间,作业要按期完成,否则每超一天扣一分,问怎样才能让他扣分最少。

    原先我想和其他dp一样找状态转移方程,但感觉状态要表示出来很困难。由于题目说作业数最多是15,因此若我们采用一个2的15次方的数来表示,每一个数为0则表示尚未完成此项作业,为1则表示已完成此项作业,这样这个状态就很好表示了。

    在这里设置一个结构体,里面要包含一个记录前驱的pre,然后自然的有最小的扣分代价,此外,由于是否在规定时间内完成了这道题目是由当前时间,截止时间和你花费时间共同决定的,那你就必须判断当前时间与截止时间的差距是多少,因此外设一个表示当前时间。

    以dp[i]来表示完成了i这个二进制数中所有为1的位置的对应的作业的状态

    至于递推条件:(1)对当前状态i进行枚举他所为0的部分,j,若(i&j==0)则说明j这样作业尚未被完成。(2)然后将其添加,变成s=i|j,在更新dp[s]。在此我们要设一个标记数组,因为我们并不知道之前s是否已经被更新。

    状压DP:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    const int N=1<<16;
    
    struct node{
        char name[110];
        int dt,cost;
    }work[30];
    
    struct DP{
        int pre,time;
        int min_cost;
    }dp[N];
    
    int n,ans[30],vis[N];
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            memset(vis,0,sizeof(vis));
            for(int i=0;i<n;i++)
                scanf("%s%d%d",work[i].name,&work[i].dt,&work[i].cost);
            dp[0].pre=-1; dp[0].time=0; dp[0].min_cost=0;
            int M=1<<n;
            int i,j,k;
            for(i=0;i<M;i++)
                for(j=0;j<n;j++){
                    k=1<<j;
                    int tmp,cost;
                    if((k&i)==0){
                        tmp=k|i;
                        cost=dp[i].time+work[j].cost-work[j].dt;
                        if(cost<0)
                            cost=0;
                        cost+=dp[i].min_cost;
                        if(vis[tmp]){
                            if(dp[tmp].min_cost>cost){
                                dp[tmp].min_cost=cost;
                                dp[tmp].pre=j;
                                dp[tmp].time=dp[i].time+work[j].cost;
                            }
                        }else{
                            vis[tmp]=1;
                            dp[tmp].min_cost=cost;
                            dp[tmp].pre=j;
                            dp[tmp].time=dp[i].time+work[j].cost;
                        }
                    }
                }
            printf("%d\n",dp[M-1].min_cost);
            k=M-1;
            for(i=0;i<n;i++){
                ans[i]=dp[k].pre;
                k=k^(1<<dp[k].pre);
            }
            for(i=n-1;i>=0;i--)
                printf("%s\n",work[ans[i]].name);
        }
        return 0;
    }
    View Code

    BFS: (还是状压快):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    const int N=1<<16;
    
    struct node{
        char name[110];
        int dt,cost;
    }work[30];
    
    struct DP{
        int time,cost,status;   //时间 罚分 完成状态   
        char s[30]; //记录顺序 课程的编号   
    }dp[N]; //下标表示状态 如dp[5] 化为 二进制 dp[101] 表示完成了编号 0 ,2的作业(从0开始) 
    
    int n;
    
    void BFS(){
        queue<DP> q;
        while(!q.empty())
            q.pop();
        DP cur,next;
        cur.time=0, cur.cost=0, cur.status=0, cur.s[0]='\0';
        dp[0]=cur;
        q.push(cur);
        while(!q.empty()){
            cur=q.front();
            q.pop();
            for(int i=0;i<n;i++){
                if((cur.status&(1<<i))==0){ //没有含有第i门课程 
                    next.status=cur.status | (1<<i);
                    next.time=cur.time+work[i].cost;
                    next.cost=cur.cost;
                    if(next.time>work[i].dt)
                        next.cost+=next.time-work[i].dt;
                    if(dp[next.status].cost==-1 || next.cost<dp[next.status].cost){ //更新   
                        strcpy(next.s,cur.s);
                        int len=strlen(next.s);
                        next.s[len]=i+'0';
                        next.s[len+1]='\0'; //注意
                        dp[next.status]=next;
                        q.push(next);
                    }
                }
            }
        }
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i=0;i<n;i++)
                scanf("%s%d%d",work[i].name,&work[i].dt,&work[i].cost);
            memset(dp,0,sizeof(dp));
            for(int i=0;i<(1<<n);i++)
                dp[i].cost=-1;
            BFS();
            int M=(1<<n)-1;
            printf("%d\n",dp[M].cost);
            for(int i=0;i<n;i++)
                printf("%s\n",work[dp[M].s[i]-'0'].name);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Laravel 禁用指定 URL POST 请求的 csrf 检查
    laravel console
    python之面向对象(一)
    python中os.popen, os.system()区别
    Day6作业:计算器
    Day5作业,商城+ATM机+后台管理
    python基础之反射
    python基础之坑爹正则表达式
    python基础之模块(一)
    python 中的一些基础算法:递归/冒泡/选择/插入
  • 原文地址:https://www.cnblogs.com/jackge/p/3078114.html
Copyright © 2020-2023  润新知