ACM的第三乐章---状态DP

poj 1185
http://poj.org/problem?id=1185
/*Problem: 1185        User: ruan123Memory: 2128K        Time: 266MSLanguage: G++        Result: Accepted说下dp方程:dp[i][j][k] 为第i行状态为Sj,第i-1行状态为Sk时前i行最多能放置的炮数。 */#include <stdio.h>#include <string.h>#include <stdlib.h>int dp[105][65][65];int s[105],map[105],cnt,c[100];void init(int m){    m = 1<<m;    for(int i = 0; i < m; ++ i){        if(!(i&(i<<1)) && !(i&(i<<2))){            int sum = 0;            int k = i;            while(k){ sum += k&1; k /= 2; }            c[cnt] = sum;            s[cnt++] = i;        }    }}int main(){    int n,m,ans = 0;    char str[100];    cnt = 0;        scanf("%d%d",&n,&m);    for(int i = 0; i < n; ++ i){        scanf("%s",str);        for(int j = 0; str[j]; ++j )            if(str[j] == 'H') map[i] += (1 << j);    }        init(m);    memset(dp,-1,sizeof(dp));    for(int i = 0; i < cnt; ++ i)        if(!(map[0]&s[i])) dp[0][i][0] = c[i];            for(int i = 1; i < n; ++ i){        for(int j = 0; j < cnt; ++ j){            if(s[j]&map[i])continue;//有冲突             for(int k = 0; k < cnt; ++ k){                if(s[j]&s[k])continue;//有冲突                for(int l = 0; l < cnt; ++ l){                    if(s[j]&s[l])continue;//有冲突                    if(dp[i-1][k][l]<0)continue;//没有值                     dp[i][j][k] = dp[i][j][k] > dp[i-1][k][l] + c[j] ? dp[i][j][k] : dp[i-1][k][l] + c[j];                }            }        }    }        for(int i = 0; i < cnt; ++ i)        for(int j = 0; j < cnt; ++ j){            ans = dp[n-1][i][j] > ans? dp[n-1][i][j] : ans;        }    printf("%d\n",ans);    return 0;}


 A Tour Around Hangzhou
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1175
/**************************************************************
    Problem: 1175
    User: aigoruan
    Language: C++
    Result: Accepted
    Time:520 ms
    Memory:14680 kb
    http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1175
    思路:dfs下的状态dp
****************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<vector>
using namespace std;
const int inf = 0xffffff;
int que[1000005],city[20],as[20][20];
int dis[10005],vis[10005];
int dp[16][1<<17];
vector<int>graph[10005];
vector<int>cost[10005];
int n,m,k,full;
void spfa(int s,int x)
{
    int ts = 0,te = 1;
    for(int i = 0; i < n; ++ i) dis[i] = inf;
    memset(vis,0,sizeof(vis));
    que[ts] = s;
    dis[s] = 0;
    while(ts<te){
        int k = que[ts];
        vis[k] = 0;
        for(int i = 0; i < graph[k].size(); ++ i){
            int t = graph[k][i];
            int v = cost[k][i];
            if( dis[t] > dis[k] + v ){
                dis[t] = dis[k] + v;
                if(!vis[t]){
                    que[te++] = t;
                    vis[t] = 1;
                }
            }
        }ts++;
    }
    
    for(int i = 0; i <= k; ++ i)
        as[x][i] = dis[city[i]];
}

int dfs(int p,int stat)
{
    if(dp[p][stat] != -1) return dp[p][stat];
    if(stat == full) return dp[p][stat] = as[p][0];
    int ans = inf, tmp;
    
    for(int i = 1; i <= k; ++ i){
        if((stat&(1<<i)) == 0){
            tmp = dfs(i,stat ^ (1 << i));//由stat状态出发,求出最优解
            if( tmp + as[p][i] < ans )
                ans = as[p][i] + tmp;
        }
    }
    return dp[p][stat] = ans;
}
int main()
{
    while(scanf("%d",&n)==1){
        scanf("%d%d",&m,&k);
        for(int i = 0; i < n; ++ i){ graph[i].clear(); cost[i].clear(); }
        int a,b,c;
        while(m--){
            scanf("%d%d%d",&a,&b,&c);
            graph[a].push_back(b);
            cost[a].push_back(c);
            graph[b].push_back(a);
            cost[b].push_back(c);
        }
        
        for(int i = 1; i <= k; ++ i)scanf("%d",city+i);
        scanf("%d",&city[0]);
        for(int i = 0; i <= k; ++ i)spfa(city[i],i);
        
        bool isok = true;
        for(int i = 0; i <= k; ++ i)
            for(int j = 0; j <= k; ++ j)
                if( as[i][j] == inf ) isok = false;
                
        if(!isok){
            puts("What a pity");
        }else{
            full = (1<<(k+1))-2;
            for(int i = 0; i <= k; ++ i)
                for(int j = 0; j <= full; ++ j)
                    dp[i][j] = -1;
            printf("%d\n",dfs(0,0));
        }
    }return 0;
}
(以上三题目都参考的网上代码,算是入门)

Painting A Board
http://poj.org/problem?id=1691

/*
Problem: 1691        User: ruan123
Memory: 168K        Time: 0MS
Language: C++        Result: Accepted
Ps:预处理当前块能画色之前那些块必须画好色了;
当要画当前块时,先判断下前面的块是否已画好; 
(同一思想,dfs应该可过)
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;

struct nd{
    int x1,y1,x2,y2,cl;
}as[20];

int s[20];
int vis[20];
int n;
bool cmp(struct nd a,struct nd b){
    if(a.y1 == b.y1 )return a.x1 < b.x1;
    return a.y1 < b.y1;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 0; i < n; ++ i)
            scanf("%d %d %d %d %d",&as[i].y1,&as[i].x1,&as[i].y2,&as[i].x2,&as[i].cl);
            
        sort(as,as+n,cmp);
        memset(s,0,sizeof(s));
        for(int i = 0; i < n; ++ i){
            for(int j = 0; j < i; ++ j){
                if(as[i].y1 == as[j].y2){
                    //预处理当前块能画色之前那些块必须画好色了
                    if((as[i].x1 <= as[j].x1 && as[i].x2 >= as[j].x1)||
                    (as[i].x1 >= as[j].x1 && as[i].x2 <= as[j].x2)){
                        s[i] += (1<<j);   
                    }
                }
            }
        }
        
        int ans = 100;
        for(int k = 0; k < n; ++ k){
            if(as[k].y1)break;
            int stat = 0;
            int tmp = 0;
            memset(vis,-1,sizeof(vis));
            for(int i = -1; i < n; ++ i){
                if(i==-1||vis[i]==-1){
                    int ii = i==-1?k:i;
                    stat += (1<<ii);
                    vis[ii] = 0;
                    ++tmp;
                    for(int j = 0; j < n; ++ j)
                        if(vis[j]==-1){
                            //当要画当前块时,先判断下前面的块是否已画好
                            if((as[j].cl == as[ii].cl) && ((s[j]&stat) == s[j]) ){
                                stat += (1<<j);
                                vis[j] = 0;
                            }
                        }
                }
            }
            if(ans > tmp) ans = tmp;
        }
        printf("%d\n",ans);
    }return 0;    
}


Rectangular Covering
http://poj.org/problem?id=2836



/*
Problem: 2836        User: ruan123
Memory: 424K        Time: 172MS
Language: C++        Result: Accepted
只有15个点,每个点用二进制中的一位来表示
预处理,枚举n*(n-1)/2个矩形,每个矩形的有自己的状态(覆盖的点)和面积
状态转移方程
for(int i = 0; i < (1<<n); ++ i)
    for(int j = 0; j < cnt; ++ j)
        dp[k] = Min(dp[k],dp[i] + val[j]);

*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int n,cnt;
int stat[300],val[300];
int x[20];
int y[20];

void getstat(int s,int t)
{
    int k = (1<<(s-1))+(1<<(t-1));
    for(int i = 1; i <= n; ++ i)
        if((x[i]-x[s])*(x[t]-x[i])>=0 && (y[i]-y[s])*(y[t]-y[i])>=0)
            k |= 1<<(i-1);
    stat[cnt] = k;
    
    int j = 1;
    if(x[s]==x[t]) j = y[s] - y[t];
    else if(y[s]==y[t]) j = x[s] - x[t];
    else j = (x[s]-x[t])*(y[s]-y[t]);
    if(j < 0)j *= -1;
    val[cnt++]=j;
}

int dp[1<<16];

int Min(int x,int y)
{
    return x > y ? y : x;
}

int main()
{
    while(scanf("%d",&n)==1)
    {
        if(!n)break;
        
        for(int i = 1; i <= n; ++ i) scanf("%d %d",x+i,y+i);
        
        cnt = 0;
        for(int i = 1; i <= n; ++ i)
            for(int j = i+1; j <= n; ++ j) getstat(i,j);
        
        memset(dp,-1,sizeof(dp));
        
        dp[0] = 0;
        for(int i = 0; i < (1<<n); ++ i){
            if(dp[i]==-1) continue;
            for(int j = 0; j < cnt; ++ j){
                int k = stat[j] | i;
                if(k == i)continue;//状态i与j无相交的 
                if(dp[k]==-1) dp[k] = dp[i] + val[j];
                else dp[k] = Min(dp[k],dp[i] + val[j]);
            }
        }
        
        printf("%d\n",dp[(1<<n)-1]);
    }return 0;
}

posted on 2012-10-30 09:16  aigoruan  阅读(194)  评论(0编辑  收藏  举报

导航