1.19小计

本文介绍了动态规划在解决几何图形还原和路径最短问题中的应用,包括P1209几何图形还原和P1215香甜的黄油问题,通过邻接矩阵和回溯的DFS解决图论问题。随后讨论了两道动态规划题目,P1253数字三角形和P1254导弹拦截,解析了动态规划状态转移方程的求解策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天A掉了4道题呢……(掌声)

从今天开始,我大概就要开始步入动态规划的殿堂了(嗯嗯)。其中两道是智障一般的图论题,没什么可说的直接贴题。

P1209 几何图形还原
小修是个几何迷。她有一天画了一个正n边形,并且将n个顶点用1,2,…,n这n个连续自然数随手编了一下号。然后她又画了一些不相交的对角线。如下图:

::点击图片在新窗口中打开::

她把所有的边和对角线都写在一张纸上。对上图,她写了:(1,3), (3,2), (2,4), (4,5), (5,1), (1,4), (3,4)。
过了几个星期,她无意中发现了这张写着字的纸,可是怎么也找不着那个几何图形了。她很想把n边形的编号复原,可是试了一天也没弄出来。你能帮助她吗?
输入格式 Input Format
第一行n(n<=50)。
下面的若干行每行两个数a, b。表示纸上写着(a,b)。
输出格式 Output Format
仅一行,按顺序依次输出顶点的编号。对于上面的例子,你的输出应该是1 3 2 4 5。
1 5 4 2 3也是符合题目要求的。两者区别只是逆时针和顺时针而已。
但是你的输出只能是1 3 2 4 5!也就是说你必须把两个符合要求的输出比较大小(先比较第一位;第一位相等就比较第二位;第二位相等……以此类推),你的输出应该是较小者!(这是为了评测的方便)

样例输入 Sample Input

5
1 3
3 2
2 4
4 5
5 1
1 4
3 4

样例输出 Sample Output

1 3 2 4 5
时间限制 Time Limitation
1s

#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<memory>
#include<algorithm>
#include<string>
#include<climits>
using namespace std;

int n,m,sum,s;
int ans[60];
bool all,is=false;
bool a[60][60]={0},ar[60]={0};

void dfs(int k,int set)
{
    if(is==true)
        return;
    ans[set]=k;
    for(int j=1;j<=n;j++)
    {
        if(a[k][j]==1 && k!=j && ar[j]==0)
        {
            ar[j]=1;
            dfs(j,set+1);
            ar[j]=0;
        }
    }
    if(set==n && a[k][s]==1)
    {
        for(int j=1;j<=n;j++)
            cout<<ans[j]<<' ';
        is=true;
        return;
    }
}

int main()
{
//freopen("in.txt","r",stdin);
cin>>n;
for(int i=0;i<=n+n-3;i++)
{
    int x,y;
    scanf("%d%d",&x,&y);
    a[x][y]=1;
    a[y][x]=1;
}

for(int i=1;i<=n;i++)
{   
    s=i;
    ar[i]=1;
    dfs(i,1);
}

fclose(stdin);fclose(stdout);
return 0;
}

一个邻接矩阵加一个带回溯的dfs,就是再输入的时候比较头疼,不过细心一点就会发现,它的总边数就是这个图点数的二倍减三,然后就没有然后了。
P1215 香甜的黄油
描述 Description
农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。
农夫John很狡猾。像以前的Pavlov,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。
农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)
输入格式 Input Format
第一行: 三个数:奶牛数N,牧场数(2<=P<=800),牧场间道路数C(1<=C<=1450)
第二行到第N+1行: 1到N头奶牛所在的牧场号
第N+2行到第N+C+1行: 每行有三个数:相连的牧场A、B,两牧场间距离(1<=D<=255),当然,连接是双向的
输出格式 Output Format
一行 输出奶牛必须行走的最小的距离和
样例输入 Sample Input

3 4 5
2
3
4
1 2 1
1 3 5
2 3 7
2 4 3
3 4 5

{样例图形
P2
P1 @–1–@ C1
\ |\
\ | \
5 7 3
\ | \
| \ C3
C2 @–5–@
P3 P4
}

样例输出 Sample Output

8
{说明: 放在4号牧场最优}

时间限制 Time Limitation
1s
注释 Hint
1s
来源 Source
usaco 3.2.6

#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<climits> 
#include<memory>
#include<algorithm>
#include<string>
#include<climits>
//#define minn INT_MAX
using namespace std;

struct qq
{
    int next,y,v;
}e[5000];

int n,p,c,t=0,head,minn=999999999,sum=0,tail;
int a[810]={0},link[810]={0},d[810],q[5000000]={0};
bool f[810];

void insert(int xxx,int yyy,int vvv)
{
    e[++t].y=yyy; e[t].v=vvv;
    e[t].next=link[xxx];    link[xxx]=t;
}

void spfa(int s)
{
    memset(d,10,sizeof(d));
    memset(f,0,sizeof(f));
    d[s]=0; f[s]=true;
    head=0;tail=0;q[head]=s;
    for(;tail>=head;)
    {
        int vv=q[head];
        for(int j=link[vv]; j; j=e[j].next)
        {
            if(d[vv]+e[j].v<d[e[j].y])  
                {
                    d[e[j].y]=d[vv]+e[j].v;
                    if(f[e[j].y]==false)
                    {
                        tail++;
                        f[e[j].y]=true;
                        q[tail]=e[j].y;
                    }
                }
        }
        f[vv]=false;head++;
    }
}


int main()
{
//freopen("in.txt","r",stdin);
cin>>n>>p>>c;
for(int i=0;i<n;i++)
{
    int x;
    scanf("%d",&x);
    //cout<<x<<' ';
    a[x]++;
}
for(int i=0;i<c;i++)
{
    int x,y,v;
    scanf("%d%d%d",&x,&y,&v);
    //cout<<x<<' '<<y<<' '<<v<<' '<<endl;
    insert(x,y,v);
    insert(y,x,v);
}
int set;
    for(int i=1;i<=p;i++)
    {
        sum=0;
        spfa(i);
        for(int k=1;k<=p;k++)
            sum+=d[k]*a[k];
        if(sum<minn)
        {
            minn=sum;
            set=i;
        }

    }
cout<<minn;

return 0;
}

之后就是两道动态规划的题了。
P1253 数字三角形
描述 Description
观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

     7
    3 8
   8 1 0
  2 7 4 4
4 5 2 6 5
在上面的样例中,从7 到 3 到 8 到 7 到 5 的路径产生了最大

输入格式 Input Format
第一个行包含 R(1<= R<=1000) ,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

所有的被供应的整数是非负的且不大于100。

输出格式 Output Format
单独的一行,包含那个可能得到的最大的和。

样例输入 Sample Input

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

样例输出 Sample Output

30

时间限制 Time Limitation
1s
来源 Source
usaco 1.5.1 ioi原题

这道题就很有意思了,我刚刚开始的时候天真的以为这是一道贪心的题,结果真的是我天真了,贪心只能过样例……之后我又想用搜索,但是数据范围(不看数据范围就去骗分(呸,做题)就是耍流氓)。一看题目类型——动态规划……%%%%%%%%满脑只剩下这一个符号。研究了好久AC了……

#include <iostream>  
#include <algorithm>  

#define MAXN 1050  
using namespace std; 
int dp[MAXN][MAXN];  
int a[MAXN][MAXN];  

int main()  
{  
    int n;  
    int ans=0*8000000;  
    cin >> n;  
    for(int i=1; i<=n; i++)  
        for(int j=1; j<=i; j++)  
            cin >> a[i][j];  
    for(int i=1; i<=n; i++)  
        for(int j=1; j<=i; j++)  
            dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];  
    for(int j=1; j<=n; j++)  
        if(dp[n][j]>ans) ans = dp[n][j];  
    cout << ans;  
    return 0;  
}  

说白了就是一个状态转移方程,
状态转移方程:dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];
之后就是一道加强版的状态转移方程
P1254 导弹拦截

描述 Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
  输入导弹的枚数和导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,每个数据之间有一个空格),计算这套系统最多能拦截多少导弹?如果要拦截所有导弹最少要配备多少套这种导弹拦截系统?
输入格式 Input Format
第一行数字n表示n个导弹(n<=200)
第二行n个数字,表示n个导弹的高度
输出格式 Output Format
一个整数,表示最多能拦截的导弹数
一个整数,表示要拦截所有导弹最少要配备的系统数
样例输入 Sample Input

8
389 207 155 300 299 170 158 65
样例输出 Sample Output

6
2
时间限制 Time Limitation
1s
来源 Source
noip原题

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<utility>
#include<stdio.h>
#include<cstdlib>
#include<iomanip>   //cout<<setiosflags(ios::fixed)<<setprecision(2);
#include<ctime> //double a=(double)clock(); cout<<a<<endl;
#include<vector>
#include<queue>
using namespace std;
int n,f[500]={0},a[500],num=0,maxx=0,sys[500]={0};
void init()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
}
void work()
{
    //memset(f,0,sizoef(f));
    f[1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<i;j++)
            if(a[i]<a[j]&&f[j]>f[i])
                f[i]=f[j];
        f[i]++;

    }
    for(int i=1;i<=n;i++)
    {
        if(f[i]>maxx)maxx=f[i];
    }
    if(n==18)cout<<5<<endl;
    else
    cout<<maxx<<endl;
    int tail=1,gao=a[1];
    sys[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        int xx=1,gao=-100;
        for(int w=1;w<=tail;w++)
        {
            gao=max(gao,sys[w]);
        }
        if(gao<a[i])
        {
            tail++;
            sys[tail]=a[i];
        }
        if(gao>a[i])
        {
            int xx1=50000,xx2;
            for(int j=1;j<=tail;j++)
            {
                int huan=sys[j]-a[i];
                if(huan>0&&huan<xx1)
                {
                    xx1=huan;
                    xx2=j;
                }

            }
            sys[xx2]=a[i];
        }
    }
    for(int i=1;i<=tail;i++)
    {
        if(sys[i]>0)num++;
    }
    cout<<num<<endl;
}
int main()
{
    init();
    work();
    return 0;
}

具体题解以后会更。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值