【日记】7.9

  这个星期以来第一次发总结,因为目前为止就今天的题可以用来总结……

 

第一题,

第一题
【题目描述】

有N块编号为1~N的特殊磁石相互吸附组成一条磁性链,只有它们紧挨着时才会传递吸力,他们之间的吸力很大,如果我们要从N块相连的磁石中取出一块,那么需要消耗N-1个单位的能量,空缺处不再有吸力传递,空出的位置也不会再被吸到一起。现在我们要取出Q块磁石,并且给出它们的编号,问最少要消耗多少单位的能量?

【输人格式】

    第一行两个数N和Q,Q表示要取走的磁石数;

    第二行Q个数,表示要取走哪些编号的磁石。

【输出格式】

    仅一行,表示最少消耗的能量。

【输入样例】

    20 3

    3 6 14

【输出样例】

    35

【数据规模】

Q≤N;

50%的数据l≤N≤1001≤Q≤5100%的数据l≤N≤10001≤Q≤100

  很简单的区间动态规划,我还无聊的加上了四边形不等式优化,结果我的快排+不等式优化被pb大神的冒泡+裸转移完爆了3倍的速度、、估计是数据规模太小的原因吧,优化现实不出来

View Code
 1 /**
 2 *Prob    : linka
 3 *Data    : 2012-7-9
 4 *Sol    : dp+四边形不等式
 5 */
 6 
 7 #include <cstring>
 8 #include <cstdio>
 9 #include <cmath>
10 #include <algorithm>
11 
12 #define MaxN 1100
13 #define MaxQ 200
14 
15 using namespace std;
16 
17 int n,q;
18 int a[MaxN],s[MaxN];
19 int f[MaxQ][MaxQ];
20 int g[MaxQ][MaxQ];
21 
22 int main()
23 {
24     freopen("linka.in","r",stdin);
25     freopen("linka.out","w",stdout);
26     
27     scanf("%d%d",&n,&q);
28     for (int i=1; i<=q; i++)
29         scanf("%d",&a[i]);
30     sort(a+1,a+1+q);
31     
32     a[0] = 0;
33     for (int i=1; i<=q; i++)
34         s[i] = a[i] - a[i-1] -1;
35     s[q+1] = n-a[q];
36     for (int i=1; i<=q+1; i++)
37         s[i] += s[i-1];
38     
39     memset(f,127,sizeof(f));
40     for (int i=1; i<=q+1; i++) {
41         g[i][i] = i;
42         f[i][i] = 0;
43     }
44     for (int i=q+1; i>=1; i--)
45      for (int j=i+1; j<=q+1; j++)
46       for (int k=g[i][j-1]; k<=g[i+1][j]; k++)
47       {
48         int tmp = f[i][k]+f[k+1][j]+(s[j]-s[i-1])+(j-i-1);
49         if (tmp<f[i][j]) {
50             g[i][j] = k;
51             f[i][j] = tmp;
52         }
53       }
54     
55     printf("%d",f[1][q+1]);
56     
57     fclose(stdin); fclose(stdout);
58     return 0;
59 }

 

第二题,

第二题
【题目描述】

一个简单的数列问题:

给定一个长度为n的数列,求这样的三个元素ai,aj,ak的个数,


满足ai〈aj〉ak,且i〈j〈k。




【输入格式】

第1行是一个整数n(1<=n<=50000)。

接下来n行,每行一个元素ai(0<=ai<=32767)。


【输出格式】



一个数,满足ai〈aj〉ak(i〈j〈k)的个数。




【输入样例】

5

1

2

3

4

1

【输出样例】

6

【数据规模】

对于30%的输入数据有n<=200。

对于80%的输入数据有n<=10000。

对于100%的输入数据有n<=50000

已经无力吐槽这个题的简单程度了,写个线段树、平衡树、树状数组或者归并排序求逆序对各种搞各种过…我用的线段树…

View Code
  1 /*Task : b
  2  *Data : 2.19 
  3  *Sol  : 线段树
  4  *Auth : Zhou Hang
  5 */
  6 
  7 #include <cstdio>
  8 #include <iostream>
  9 #include <cstring>
 10 #include <algorithm>
 11 
 12 using namespace std;
 13 
 14 const int MaxNum=40000;
 15 const int MaxN=51000;
 16 const int oo=1000000000;
 17 
 18 int a[4*MaxNum+100][2];
 19 int m,n,ans,i,x,y,ch;
 20 int l[MaxN],r[MaxN],h[MaxN];
 21 
 22 void updata(int l,int r,int root)
 23 {
 24     if ((y<l)||(x>r)) return;
 25     
 26     if ((x<=l)&&(y>=r))
 27     {
 28         a[root][0]++; a[root][1]++;
 29         return;
 30     }
 31     
 32     int mid=(l+r)/2;
 33     //必须是临时变量,避免递归时改变
 34     
 35     //left child
 36     a[root*2][0]+=a[root][1]; 
 37     a[root*2][1]+=a[root][1];
 38     //right child
 39     a[root*2+1][0]+=a[root][1];
 40     a[root*2+1][1]+=a[root][1];
 41     //itself
 42     a[root][1]=0;
 43     
 44     updata(l,mid,root*2);
 45     updata(mid+1,r,root*2+1);
 46     //change Sum
 47     a[root][0]=a[root*2][0]+a[root*2+1][0];
 48 }
 49 
 50 int find(int l,int r,int root)
 51 {
 52     if ((y<l)||(x>r)) return 0;
 53     if ((x<=l)&&(y>=r)) return a[root][0];
 54     
 55     int mid=(l+r)/2;
 56     //left child
 57     a[root*2][0]+=a[root][1]; 
 58     a[root*2][1]+=a[root][1];
 59     //right child
 60     a[root*2+1][0]+=a[root][1];
 61     a[root*2+1][1]+=a[root][1];
 62     //itself
 63     a[root][1]=0;
 64     
 65     int m1=find(l,mid,root*2);
 66     int m2=find(mid+1,r,root*2+1);
 67     
 68     return m1+m2;
 69 }
 70 
 71 int main()
 72 {
 73     
 74     freopen("queueb.in","r",stdin);
 75     freopen("queueb.out","w",stdout);
 76     
 77     scanf("%d",&n);
 78     
 79     int maxh = -1;
 80     for (int i=1; i<=n; i++) {
 81         scanf("%d",&h[i]);
 82         if (h[i]>maxh) maxh = h[i];
 83     }
 84     
 85     for (int i=1; i<=n; i++) {
 86         x = y = h[i];
 87         updata(0,maxh,1);
 88         x = 0; y = h[i]-1;
 89         l[i] = find(0,maxh,1);
 90     }
 91     memset(a,0,sizeof(a));
 92     for (int i=n; i>=1; i--) {
 93         x = y = h[i];
 94         updata(0,maxh,1);
 95         x = 0; y = h[i]-1;
 96         r[i] = find(0,maxh,1);
 97     }
 98     
 99     long long ans = 0;
100     for (int i=1; i<=n; i++)
101         ans += (long long)l[i]*r[i];
102     
103     cout<<ans<<endl;
104     
105     fclose(stdin); fclose(stdout);
106     
107 }

 

第三题,

第三题
【题目描述】

某商品推销员到某小区推销产品,产品推销除了商品质量好之外,还需要客户能影响和带动身边信赖他的人也购买,如果A购买了产品,那么信赖A的B就有可能也购买,那么信赖B的C就也可能成为潜在客户,依据这种信赖关系,就可能构成一个庞大的潜在客户网。聪明的推销员经过细心调查整理了这个小区里N个人的信赖关系,并且用1~N给这N个人编号。另外,这N个人中有P个潜在客户可能会被推销员直接说服购买产品,细心的推销员还估算了说服每个人具体需要花费的时间,那么请你帮推销员计算一下他最少需要花费多长时间来建立起这N个人的潜在客户网?如果不能把这N个人全部纳入潜在客户网,输出不能被纳入网络的人的编号。注意,信赖关系不一定是相互的啊!


【输人格式】

输入文件第一行只有一个整数n(n<=3000)。

    第二行是整数p。表示能被说服的人数,1≤p≤n。

    接下来的p行,每行有两个整数,第一个数是一个能被说服的人的编号,第二个数表示他被说服需要花费的时间。这个数不超过20000个时间单位。

紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),B信赖A。


【输出格式】

如果可以把N个人全部纳入潜在客户网,第一行输出YES,并在第二行输出所需要花费的最少说服时间。否则输出NO,并在第二行输出不能纳入网络的人编号中,编号最小的。


【输入样例】

Example1:

3

2

1 10

2 100

2

1 3

2 3

 

Example2:

4

2

1 100

4 200

2

1 2

3 4


【输出样例】

Example1:

YES

110

Example2:

NO

3

【数据规模】

对于30%的输入数据有n<=10。

对于100%的输入数据有n<=3000

唯一没有AC的题目……超时了两组。

加入一个超级源,能直接说服的就向他连边,权值为时间,对于数对(A,B),连边,权值为0,然后就是有向图的最小生成树,也就是最小树形图,根本不会……然后百度了一下,现学现写的,估计是因为用的邻接矩阵,所以时间复杂度到了O(N^3),超时了一两组,如果变成邻接表应该好很多。但是我已经不想再改了……先仍在这里了

View Code
  1 /**
  2 *Prob    :    salenet
  3 *Data    : 2012-7-9
  4 *Sol    : 骗分
  5 */
  6 
  7 #include <cstdio>
  8 #include <cstring>
  9 #include <cmath>
 10 
 11 #define MaxN 3010
 12 #define oo 200000000
 13 
 14 using namespace std;
 15 
 16 int in[MaxN];
 17 int n,count;
 18 bool used[MaxN],d[MaxN][MaxN];
 19 int a[MaxN][MaxN],mindis[MaxN];
 20 
 21 //dfs判断存在性
 22 void dfs(int x)
 23 {
 24     used[x] = true; count++;
 25     for (int i=1; i<=n; i++)
 26         if (d[x][i]&&!used[i]) dfs(i);
 27 }
 28 
 29 //寻找环,返回其中的一个点
 30 int find_circle(int x)
 31 {
 32     bool v[MaxN];
 33     memset(v,0,sizeof(v));
 34     int t = in[x];
 35     v[x] = true;
 36     while (t&&!v[in[t]])
 37         v[t] = true, t = in[t];
 38     return t;
 39 }
 40 
 41 int min_tree_pic()
 42 {
 43     int i,j,k,u,tt,t,ans = 0;
 44     memset(used,false,sizeof(used));
 45     count = 0; dfs(0);
 46     if (count<n+1) return -1;
 47     memset(used,false,sizeof(used));
 48     //初始in数组
 49     for (i=1,k=0,in[0]=-1; i<=n; i++) {
 50         for (j=0,t=oo; j<=n; j++)
 51             if (!used[j]&&d[j][i]&&t>a[j][i]) {
 52                 t = a[j][i]; k = j;
 53             }
 54         in[i] = k; mindis[i] = t;
 55     }
 56     while (true) {
 57         for (i=1; i<=n; i++)
 58          if (!used[i]&&(tt=find_circle(i))) {
 59             i = tt;
 60             u = in[i];
 61             ans += mindis[i];
 62             while (u-i) {
 63                 used[u] = true;
 64                 ans += mindis[u]; u = in[u];
 65             }
 66             for (j=0; j<=n; j++)
 67              if (!used[j]&&j-i)
 68                 a[j][i] -= mindis[i];
 69             for (j=0; j<=n; j++)
 70              if (!used[j]&&j-i) {
 71                 for (u=in[i]; u-i; u=in[u]) {
 72                     if(d[u][j]&&(a[u][j]<a[i][j]||!d[i][j]))   //更新dis[i][j]
 73                         a[i][j]=a[u][j],d[i][j]=true;
 74                     if(d[j][u]&&(a[j][u]-mindis[u]<a[j][i]||!d[j][i]))//更新dis[j][i]
 75                         a[j][i]=a[j][u]-mindis[u],d[j][i]=true;                
 76                 }
 77              }
 78             break;
 79          }
 80         if (i>n) break;
 81         for(i=1,in[0]=-1;i<=n;i++)if(!used[i])  //需要重新生成in
 82         {
 83             for(j=0,t=oo,k=0;j<=n;j++)if(!used[j]&&d[j][i]&&t>a[j][i])
 84             t=a[j][i],k=j;
 85             in[i]=k;mindis[i]=t;
 86         }
 87     }
 88     for(i=1;i<=n;i++)if(!used[i])   //加上最后不成环的结点的mindis值
 89         ans+=mindis[i]; 
 90     return ans;
 91 }
 92 
 93 int main()
 94 {
 95     freopen("salenet.in","r",stdin);
 96     freopen("salenet.out","w",stdout);
 97     
 98     int p,r,x,y,z;
 99     
100     scanf("%d",&n);
101 
102     memset(d,false,sizeof(d));
103     memset(a,0,sizeof(a));
104     scanf("%d",&p);
105     for (int i=1; i<=p; i++) {
106         scanf("%d%d",&y,&z);
107         a[0][y] = z;
108         d[0][y] = true;
109     }
110     scanf("%d",&r);
111     for (int i=1; i<=r; i++) {
112         scanf("%d%d",&x,&y);
113         a[x][y] = 0; d[x][y] = true;
114     }
115     for (int i=0; i<=n; i++)
116         d[i][i] = 0;
117     
118     int ans = min_tree_pic();
119     if (ans<0) {
120         printf("NO\n");
121         for (int i=1; i<=n; i++)
122             if (!used[i]) { printf("%d\n",i); break;}
123     }
124     else printf("YES\n%d\n",ans);
125     
126     fclose(stdin); fclose(stdout);
127     return 0;
128 }

 

 

难度不大,不过考点很全面、有dp,数据结构和图论。

posted @ 2012-07-09 16:20  守護N1身边  阅读(181)  评论(0编辑  收藏  举报