今天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;
}
具体题解以后会更。