XTU算法專題個人賽3 (DP專題)解題報告

寫這個報告的目的是提醒自己,自己曾經爆0

第一題 HDU1176 免費餡餅

很容易想到dp[時間][位置]

遞歸方程很簡單,而我爲什麼沒寫出來呢。
想想當時我一直卡在怎麼存數據哪裏,遞歸什麼都想出來了。
爲什麼卡在存數據哪裏
智商······
最後注意一下初始化

AC代碼

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int pos[100010][11];
int dp[100010][11];
const int inf = 0x3f3f3f3f;
int maxt(int x,int y)
{
    if(y==0) return max(dp[x][y],dp[x][y+1]);
    if(y==10) return max(dp[x][y],dp[x][y-1]);
    return max(max(dp[x][y],dp[x][y+1]),dp[x][y-1]);
}

int main()
{
    int n;
    while(scanf("%d",&n)&&n){
        memset(pos,0,sizeof(pos));
        for(int i=0,x,y;i<n;i++){
            scanf("%d%d",&x,&y);
            pos[y][x]++;
        }

        memset(dp,0,sizeof(dp));
        for(int i=0;i<=100000;i++) for(int j=0;j<=10;j++) dp[i][j] = -inf;
        dp[0][5] = 0;
        for(int i=1;i<=100000;i++){
            for(int j=0;j<=10;j++){
                dp[i][j] = maxt(i-1,j) + pos[i][j];
            }
        }

        int ans = 0;
        for(int i=0;i<=10;i++) ans = max(ans,dp[100000][i]);
        printf("%d\n",ans);
    }
    return 0;


}

第二道 HDU 1428 漫步校園

這道題說是簡單,但是自己總是錯在一些小細節上。
並且,當時第三題卡了,心情一直不好。
我覺得這個是不應該的,是個人問題。
這題我當時好好看是會做的。

就是首先BFS一遍把每個點到終點的最遠距離記錄下來。
然後DFS記憶化搜索時剪支就行了。

下面是代碼

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
long long mp[101][101];
long long dp[101][101];
const int dx[] = {0,0,1,-1};
const int dy[] = {1,-1,0,0};
int vis[51][51];
int n;


struct po
{
    int x,y,v;
    friend bool operator < (const po a,const po b){
        return a.v > b.v;
    }
};
void bfs()
{
    memset(vis,0,sizeof(vis));
    po a = {n-1,n-1,mp[n-1][n-1]};
    vis[n-1][n-1] = 1;
    priority_queue<po> q;
    q.push(a);
    while(!q.empty()){
        po v = q.top();q.pop();
        for(int i=0;i<4;i++){
            int xn = v.x + dx[i];
            int yn = v.y + dy[i];
            if(xn < 0 || yn < 0 || xn >=n || yn >= n) continue;
            if(vis[xn][yn]) continue;
            vis[xn][yn] = 1;
            int vn = v.v + mp[xn][yn];
            po u = {xn,yn,vn};
            mp[xn][yn] = vn;
            q.push(u);
        }
    }
}

long long dfs(long long x,long long y)
{
    long long  ans = 0;
    if(!dp[x][y]){
        for(int i=0;i<4;i++){
            int xn = x + dx[i];
            int yn = y + dy[i];
            if(xn <  0 || yn < 0 || xn >= n || yn >=n) continue;
            if(mp[xn][yn] >= mp[x][y]) continue;
            ans += dfs(xn,yn);
        }

        dp[x][y] = ans;
    }
    return dp[x][y];
}


int main()
{
    while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                scanf("%lld",&mp[i][j]);
            }
        }
        bfs();
        dp[n-1][n-1] = 1;
        long long ans = dfs(0,0);
        printf("%lld\n",ans);
    }
    return 0;



}

第三題 HDU 5410 CRB and His Birthday

這道題是我最大的失敗,明明知道要把A[i] 和 B[i] 分開,但是爲什麼一直沒想出來

首先得想出來取任何一個物品都會得到一個B
這樣的話,我們就可以把A[i] + B[i]看成是一個01揹包
然後再對 A[i] 進行完全揹包

下面是代碼

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;


int a[2020],b[2020],w[2020];
int dp[2020];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int m,n;
        scanf("%d%d",&m,&n);
        for(int i=0;i<n;i++) scanf("%d%d%d",&w[i],&a[i],&b[i]);
        memset(dp,0,sizeof(dp));

        for(int i=0;i<n;i++){
            for(int j=m;j>=w[i];j--){
                dp[j] = max(dp[j-w[i]] + a[i] + b[i] ,dp[j]);
            }
        }

        for(int i=0;i<n;i++){
            for(int j=w[i];j<=m;j++){
                dp[j] = max(dp[j],dp[j-w[i]] + a[i]);
            }
        }

        printf("%d\n",dp[m]);
    }
    return 0;

}

第四題 CodeForces 543A Writing Code

這題主要完成一個轉換,一開始定義dp[前i個程序員][寫了j行代碼][犯了k個錯]

遞推方程就知道很簡單了,但是這樣會錯,還要優化。
仔細一看就知道可以優化成dp[寫了j行代碼][犯了k個錯]

下面是代碼

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;


int a[510];
int dp[510][510];

int main()
{
    int n,m,b,mod;
    scanf("%d%d%d%d",&n,&m,&b,&mod);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    dp[0][0] = 1;

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k = a[i];k<=b;k++){
                dp[j][k] += dp[j-1][k-a[i]];
                dp[j][k] %= mod;
            }
        }
    }
    int ans = 0;
    for(int i=0;i<=b;i++){
        ans += dp[m][i] ;
        ans %= mod;
    }
    printf("%d\n",ans);
    return 0;

}

第五題 HDU 5418 Victor and World

這題需要狀態壓縮,並且用floyd算法先算出每個點到另外一個地方的最小距離,因爲你中途經過那些點答案並不需要你輸出

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#define inf 0x1f1f1f1f
using namespace std;


int dp[(1<<17)][20];
int mp[20][20];


int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++){
            mp[i][i] = 0;
            for(int j=1;j<=n;j++){
                if(i!=j) mp[i][j] = inf;
            }
        }
        for(int i=0,x,y,z;i<m;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(mp[x][y]>z) mp[x][y] = mp[y][x] = z;
        }

        dp[1][1] = 0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++) mp[j][k] = min(mp[j][k] , mp[j][i] + mp[i][k]);

        int tot = (1<<n)-1;
        for(int i=1;i<=tot;i++){
            for(int j=1;j<=n;j++){ // st
                if(i&(1<<(j-1))){
                    for(int k=1;k<=n;k++){
                        if(k==j) continue;
                        if(i&(1<<(k-1))) continue;
                        else dp[i | (1<<(k-1))][k] = min(dp[i | (1<<(k-1))][k] , dp[i][j] + mp[j][k]);
                        //printf("%d\n",dp[i][k]);
                    }
                }
            }
        }

        int ans = inf;
        for(int i=1;i<=n;i++) ans = min(ans, dp[tot][i] + mp[i][1]);
        printf("%d\n",ans);
        }
        return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章